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-2013, 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
117 if (Source
> mGicNumInterrupts
) {
119 return EFI_UNSUPPORTED
;
122 // Calculate enable register offset and bit position
123 RegOffset
= Source
/ 32;
124 RegShift
= Source
% 32;
126 // Write set-enable register
127 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDISER
+ (4*RegOffset
), 1 << RegShift
);
133 Disable interrupt source Source.
135 @param This Instance pointer for this protocol
136 @param Source Hardware source of the interrupt
138 @retval EFI_SUCCESS Source interrupt disabled.
139 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
144 DisableInterruptSource (
145 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
146 IN HARDWARE_INTERRUPT_SOURCE Source
152 if (Source
> mGicNumInterrupts
) {
154 return EFI_UNSUPPORTED
;
157 // Calculate enable register offset and bit position
158 RegOffset
= Source
/ 32;
159 RegShift
= Source
% 32;
161 // Write set-enable register
162 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDICER
+ (4*RegOffset
), 1 << RegShift
);
168 Return current state of interrupt source Source.
170 @param This Instance pointer for this protocol
171 @param Source Hardware source of the interrupt
172 @param InterruptState TRUE: source enabled, FALSE: source disabled.
174 @retval EFI_SUCCESS InterruptState is valid
175 @retval EFI_DEVICE_ERROR InterruptState is not valid
180 GetInterruptSourceState (
181 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
182 IN HARDWARE_INTERRUPT_SOURCE Source
,
183 IN BOOLEAN
*InterruptState
189 if (Source
> mGicNumInterrupts
) {
191 return EFI_UNSUPPORTED
;
194 // calculate enable register offset and bit position
195 RegOffset
= Source
/ 32;
196 RegShift
= Source
% 32;
198 if ((MmioRead32 (PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDISER
+ (4*RegOffset
)) & (1<<RegShift
)) == 0) {
199 *InterruptState
= FALSE
;
201 *InterruptState
= TRUE
;
208 Signal to the hardware that the End Of Intrrupt state
211 @param This Instance pointer for this protocol
212 @param Source Hardware source of the interrupt
214 @retval EFI_SUCCESS Source interrupt EOI'ed.
215 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
221 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
222 IN HARDWARE_INTERRUPT_SOURCE Source
225 if (Source
> mGicNumInterrupts
) {
227 return EFI_UNSUPPORTED
;
230 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCEIOR
, Source
);
235 EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.
237 @param InterruptType Defines the type of interrupt or exception that
238 occurred on the processor.This parameter is processor architecture specific.
239 @param SystemContext A pointer to the processor context when
240 the interrupt occurred on the processor.
247 IrqInterruptHandler (
248 IN EFI_EXCEPTION_TYPE InterruptType
,
249 IN EFI_SYSTEM_CONTEXT SystemContext
253 HARDWARE_INTERRUPT_HANDLER InterruptHandler
;
255 GicInterrupt
= MmioRead32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCIAR
);
257 // Special Interrupts (ID1020-ID1023) have an Interrupt ID greater than the number of interrupt (ie: Spurious interrupt).
258 if (GicInterrupt
>= mGicNumInterrupts
) {
259 // The special interrupt do not need to be acknowledge
263 InterruptHandler
= gRegisteredInterruptHandlers
[GicInterrupt
];
264 if (InterruptHandler
!= NULL
) {
265 // Call the registered interrupt handler.
266 InterruptHandler (GicInterrupt
, SystemContext
);
268 DEBUG ((EFI_D_ERROR
, "Spurious GIC interrupt: 0x%x\n", GicInterrupt
));
271 EndOfInterrupt (&gHardwareInterruptProtocol
, GicInterrupt
);
275 // Making this global saves a few bytes in image size
277 EFI_HANDLE gHardwareInterruptHandle
= NULL
;
280 // The protocol instance produced by this driver
282 EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol
= {
283 RegisterInterruptSource
,
284 EnableInterruptSource
,
285 DisableInterruptSource
,
286 GetInterruptSourceState
,
291 Shutdown our hardware
293 DXE Core will disable interrupts and turn off the timer and disable interrupts
294 after all the event handlers have run.
296 @param[in] Event The Event that is being processed
297 @param[in] Context Event Context
301 ExitBootServicesEvent (
308 // Acknowledge all pending interrupts
309 for (Index
= 0; Index
< mGicNumInterrupts
; Index
++) {
310 DisableInterruptSource (&gHardwareInterruptProtocol
, Index
);
313 for (Index
= 0; Index
< mGicNumInterrupts
; Index
++) {
314 EndOfInterrupt (&gHardwareInterruptProtocol
, Index
);
317 // Disable Gic Interface
318 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCICR
, 0x0);
319 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCPMR
, 0x0);
321 // Disable Gic Distributor
322 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDDCR
, 0x0);
326 Initialize the state information for the CPU Architectural Protocol
328 @param ImageHandle of the loaded driver
329 @param SystemTable Pointer to the System Table
331 @retval EFI_SUCCESS Protocol registered
332 @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
333 @retval EFI_DEVICE_ERROR Hardware problems
337 InterruptDxeInitialize (
338 IN EFI_HANDLE ImageHandle
,
339 IN EFI_SYSTEM_TABLE
*SystemTable
346 EFI_CPU_ARCH_PROTOCOL
*Cpu
;
349 // Check PcdGicPrimaryCoreId has been set in case the Primary Core is not the core 0 of Cluster 0
351 if ((PcdGet32(PcdArmPrimaryCore
) != 0) && (PcdGet32 (PcdGicPrimaryCoreId
) == 0)) {
352 DEBUG((EFI_D_WARN
,"Warning: the PCD PcdGicPrimaryCoreId does not seem to be set up for the configuration.\n"));
356 // Make sure the Interrupt Controller Protocol is not already installed in the system.
357 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL
, &gHardwareInterruptProtocolGuid
);
359 mGicNumInterrupts
= ArmGicGetMaxNumInterrupts (PcdGet32(PcdGicDistributorBase
));
361 for (Index
= 0; Index
< mGicNumInterrupts
; Index
++) {
362 DisableInterruptSource (&gHardwareInterruptProtocol
, Index
);
365 RegOffset
= Index
/ 4;
366 RegShift
= (Index
% 4) * 8;
368 PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDIPR
+ (4*RegOffset
),
370 ARM_GIC_DEFAULT_PRIORITY
<< RegShift
374 // Configure interrupts for Primary Cpu
375 CpuTarget
= (1 << PcdGet32 (PcdGicPrimaryCoreId
));
376 CpuTarget
|= (CpuTarget
<< 24) | (CpuTarget
<< 16) | (CpuTarget
<< 8);
377 for (Index
= 0; Index
< (mGicNumInterrupts
/ 4); Index
++) {
378 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDIPTR
+ (Index
*4), CpuTarget
);
381 // Set binary point reg to 0x7 (no preemption)
382 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCBPR
, 0x7);
384 // Set priority mask reg to 0xff to allow all priorities through
385 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCPMR
, 0xff);
387 // Enable gic cpu interface
388 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCICR
, 0x1);
390 // Enable gic distributor
391 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDDCR
, 0x1);
393 // Initialize the array for the Interrupt Handlers
394 gRegisteredInterruptHandlers
= (HARDWARE_INTERRUPT_HANDLER
*)AllocateZeroPool (sizeof(HARDWARE_INTERRUPT_HANDLER
) * mGicNumInterrupts
);
396 Status
= gBS
->InstallMultipleProtocolInterfaces (
397 &gHardwareInterruptHandle
,
398 &gHardwareInterruptProtocolGuid
, &gHardwareInterruptProtocol
,
401 ASSERT_EFI_ERROR (Status
);
404 // Get the CPU protocol that this driver requires.
406 Status
= gBS
->LocateProtocol(&gEfiCpuArchProtocolGuid
, NULL
, (VOID
**)&Cpu
);
407 ASSERT_EFI_ERROR(Status
);
410 // Unregister the default exception handler.
412 Status
= Cpu
->RegisterInterruptHandler(Cpu
, ARM_ARCH_EXCEPTION_IRQ
, NULL
);
413 ASSERT_EFI_ERROR(Status
);
416 // Register to receive interrupts
418 Status
= Cpu
->RegisterInterruptHandler(Cpu
, ARM_ARCH_EXCEPTION_IRQ
, IrqInterruptHandler
);
419 ASSERT_EFI_ERROR(Status
);
421 // Register for an ExitBootServicesEvent
422 Status
= gBS
->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES
, TPL_NOTIFY
, ExitBootServicesEvent
, NULL
, &EfiExitBootServicesEvent
);
423 ASSERT_EFI_ERROR (Status
);