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-2012, 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/BaseLib.h>
28 #include <Library/DebugLib.h>
29 #include <Library/BaseMemoryLib.h>
30 #include <Library/MemoryAllocationLib.h>
31 #include <Library/UefiBootServicesTableLib.h>
32 #include <Library/UefiLib.h>
33 #include <Library/PcdLib.h>
34 #include <Library/IoLib.h>
35 #include <Library/ArmGicLib.h>
37 #include <Protocol/Cpu.h>
38 #include <Protocol/HardwareInterrupt.h>
40 #define ARM_GIC_DEFAULT_PRIORITY 0x80
42 extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol
;
47 EFI_EVENT EfiExitBootServicesEvent
= (EFI_EVENT
)NULL
;
49 // Maximum Number of Interrupts
50 UINTN mGicNumInterrupts
= 0;
52 HARDWARE_INTERRUPT_HANDLER
*gRegisteredInterruptHandlers
= NULL
;
55 Register Handler for the specified interrupt source.
57 @param This Instance pointer for this protocol
58 @param Source Hardware source of the interrupt
59 @param Handler Callback for interrupt. NULL to unregister
61 @retval EFI_SUCCESS Source was updated to support Handler.
62 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
67 RegisterInterruptSource (
68 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
69 IN HARDWARE_INTERRUPT_SOURCE Source
,
70 IN HARDWARE_INTERRUPT_HANDLER Handler
73 if (Source
> mGicNumInterrupts
) {
75 return EFI_UNSUPPORTED
;
78 if ((Handler
== NULL
) && (gRegisteredInterruptHandlers
[Source
] == NULL
)) {
79 return EFI_INVALID_PARAMETER
;
82 if ((Handler
!= NULL
) && (gRegisteredInterruptHandlers
[Source
] != NULL
)) {
83 return EFI_ALREADY_STARTED
;
86 gRegisteredInterruptHandlers
[Source
] = Handler
;
88 // If the interrupt handler is unregistered then disable the interrupt
90 return This
->DisableInterruptSource (This
, Source
);
92 return This
->EnableInterruptSource (This
, Source
);
97 Enable interrupt source Source.
99 @param This Instance pointer for this protocol
100 @param Source Hardware source of the interrupt
102 @retval EFI_SUCCESS Source interrupt enabled.
103 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
108 EnableInterruptSource (
109 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
110 IN HARDWARE_INTERRUPT_SOURCE Source
116 if (Source
> mGicNumInterrupts
) {
118 return EFI_UNSUPPORTED
;
121 // Calculate enable register offset and bit position
122 RegOffset
= Source
/ 32;
123 RegShift
= Source
% 32;
125 // Write set-enable register
126 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDISER
+ (4*RegOffset
), 1 << RegShift
);
132 Disable interrupt source Source.
134 @param This Instance pointer for this protocol
135 @param Source Hardware source of the interrupt
137 @retval EFI_SUCCESS Source interrupt disabled.
138 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
143 DisableInterruptSource (
144 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
145 IN HARDWARE_INTERRUPT_SOURCE Source
151 if (Source
> mGicNumInterrupts
) {
153 return EFI_UNSUPPORTED
;
156 // Calculate enable register offset and bit position
157 RegOffset
= Source
/ 32;
158 RegShift
= Source
% 32;
160 // Write set-enable register
161 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDICER
+ (4*RegOffset
), 1 << RegShift
);
167 Return current state of interrupt source Source.
169 @param This Instance pointer for this protocol
170 @param Source Hardware source of the interrupt
171 @param InterruptState TRUE: source enabled, FALSE: source disabled.
173 @retval EFI_SUCCESS InterruptState is valid
174 @retval EFI_DEVICE_ERROR InterruptState is not valid
179 GetInterruptSourceState (
180 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
181 IN HARDWARE_INTERRUPT_SOURCE Source
,
182 IN BOOLEAN
*InterruptState
188 if (Source
> mGicNumInterrupts
) {
190 return EFI_UNSUPPORTED
;
193 // calculate enable register offset and bit position
194 RegOffset
= Source
/ 32;
195 RegShift
= Source
% 32;
197 if ((MmioRead32 (PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDISER
+ (4*RegOffset
)) & (1<<RegShift
)) == 0) {
198 *InterruptState
= FALSE
;
200 *InterruptState
= TRUE
;
207 Signal to the hardware that the End Of Intrrupt state
210 @param This Instance pointer for this protocol
211 @param Source Hardware source of the interrupt
213 @retval EFI_SUCCESS Source interrupt EOI'ed.
214 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
220 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
221 IN HARDWARE_INTERRUPT_SOURCE Source
224 if (Source
> mGicNumInterrupts
) {
226 return EFI_UNSUPPORTED
;
229 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCEIOR
, Source
);
234 EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.
236 @param InterruptType Defines the type of interrupt or exception that
237 occurred on the processor.This parameter is processor architecture specific.
238 @param SystemContext A pointer to the processor context when
239 the interrupt occurred on the processor.
246 IrqInterruptHandler (
247 IN EFI_EXCEPTION_TYPE InterruptType
,
248 IN EFI_SYSTEM_CONTEXT SystemContext
252 HARDWARE_INTERRUPT_HANDLER InterruptHandler
;
254 GicInterrupt
= MmioRead32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCIAR
);
256 // Special Interrupts (ID1020-ID1023) have an Interrupt ID greater than the number of interrupt (ie: Spurious interrupt).
257 if (GicInterrupt
>= mGicNumInterrupts
) {
258 // The special interrupt do not need to be acknowledge
262 InterruptHandler
= gRegisteredInterruptHandlers
[GicInterrupt
];
263 if (InterruptHandler
!= NULL
) {
264 // Call the registered interrupt handler.
265 InterruptHandler (GicInterrupt
, SystemContext
);
267 DEBUG ((EFI_D_ERROR
, "Spurious GIC interrupt: 0x%x\n", GicInterrupt
));
270 EndOfInterrupt (&gHardwareInterruptProtocol
, GicInterrupt
);
274 // Making this global saves a few bytes in image size
276 EFI_HANDLE gHardwareInterruptHandle
= NULL
;
279 // The protocol instance produced by this driver
281 EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol
= {
282 RegisterInterruptSource
,
283 EnableInterruptSource
,
284 DisableInterruptSource
,
285 GetInterruptSourceState
,
290 Shutdown our hardware
292 DXE Core will disable interrupts and turn off the timer and disable interrupts
293 after all the event handlers have run.
295 @param[in] Event The Event that is being processed
296 @param[in] Context Event Context
300 ExitBootServicesEvent (
307 // Acknowledge all pending interrupts
308 for (Index
= 0; Index
< mGicNumInterrupts
; Index
++) {
309 DisableInterruptSource (&gHardwareInterruptProtocol
, Index
);
312 for (Index
= 0; Index
< mGicNumInterrupts
; Index
++) {
313 EndOfInterrupt (&gHardwareInterruptProtocol
, Index
);
316 // Disable Gic Interface
317 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCICR
, 0x0);
318 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCPMR
, 0x0);
320 // Disable Gic Distributor
321 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDDCR
, 0x0);
325 Initialize the state information for the CPU Architectural Protocol
327 @param ImageHandle of the loaded driver
328 @param SystemTable Pointer to the System Table
330 @retval EFI_SUCCESS Protocol registered
331 @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
332 @retval EFI_DEVICE_ERROR Hardware problems
336 InterruptDxeInitialize (
337 IN EFI_HANDLE ImageHandle
,
338 IN EFI_SYSTEM_TABLE
*SystemTable
345 EFI_CPU_ARCH_PROTOCOL
*Cpu
;
348 // Check PcdGicPrimaryCoreId has been set in case the Primary Core is not the core 0 of Cluster 0
350 if ((PcdGet32(PcdArmPrimaryCore
) != 0) && (PcdGet32 (PcdGicPrimaryCoreId
) == 0)) {
351 DEBUG((EFI_D_WARN
,"Warning: the PCD PcdGicPrimaryCoreId does not seem to be set up for the configuration.\n"));
355 // Make sure the Interrupt Controller Protocol is not already installed in the system.
356 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL
, &gHardwareInterruptProtocolGuid
);
358 mGicNumInterrupts
= ArmGicGetMaxNumInterrupts (PcdGet32(PcdGicDistributorBase
));
360 for (Index
= 0; Index
< mGicNumInterrupts
; Index
++) {
361 DisableInterruptSource (&gHardwareInterruptProtocol
, Index
);
364 RegOffset
= Index
/ 4;
365 RegShift
= (Index
% 4) * 8;
367 PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDIPR
+ (4*RegOffset
),
369 ARM_GIC_DEFAULT_PRIORITY
<< RegShift
373 // Configure interrupts for Primary Cpu
374 CpuTarget
= (1 << PcdGet32 (PcdGicPrimaryCoreId
));
375 CpuTarget
|= (CpuTarget
<< 24) | (CpuTarget
<< 16) | (CpuTarget
<< 8);
376 for (Index
= 0; Index
< (mGicNumInterrupts
/ 4); Index
++) {
377 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDIPTR
+ (Index
*4), CpuTarget
);
380 // Set binary point reg to 0x7 (no preemption)
381 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCBPR
, 0x7);
383 // Set priority mask reg to 0xff to allow all priorities through
384 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCPMR
, 0xff);
386 // Enable gic cpu interface
387 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCICR
, 0x1);
389 // Enable gic distributor
390 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDDCR
, 0x1);
392 // Initialize the array for the Interrupt Handlers
393 gRegisteredInterruptHandlers
= (HARDWARE_INTERRUPT_HANDLER
*)AllocateZeroPool (sizeof(HARDWARE_INTERRUPT_HANDLER
) * mGicNumInterrupts
);
395 Status
= gBS
->InstallMultipleProtocolInterfaces (
396 &gHardwareInterruptHandle
,
397 &gHardwareInterruptProtocolGuid
, &gHardwareInterruptProtocol
,
400 ASSERT_EFI_ERROR (Status
);
403 // Get the CPU protocol that this driver requires.
405 Status
= gBS
->LocateProtocol(&gEfiCpuArchProtocolGuid
, NULL
, (VOID
**)&Cpu
);
406 ASSERT_EFI_ERROR(Status
);
409 // Unregister the default exception handler.
411 Status
= Cpu
->RegisterInterruptHandler(Cpu
, EXCEPT_ARM_IRQ
, NULL
);
412 ASSERT_EFI_ERROR(Status
);
415 // Register to receive interrupts
417 Status
= Cpu
->RegisterInterruptHandler(Cpu
, EXCEPT_ARM_IRQ
, IrqInterruptHandler
);
418 ASSERT_EFI_ERROR(Status
);
420 // Register for an ExitBootServicesEvent
421 Status
= gBS
->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES
, TPL_NOTIFY
, ExitBootServicesEvent
, NULL
, &EfiExitBootServicesEvent
);
422 ASSERT_EFI_ERROR (Status
);