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>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
20 Driver implementing the GIC interrupt controller protocol
26 #include <Library/BaseLib.h>
27 #include <Library/DebugLib.h>
28 #include <Library/BaseMemoryLib.h>
29 #include <Library/UefiBootServicesTableLib.h>
30 #include <Library/UefiLib.h>
31 #include <Library/PcdLib.h>
32 #include <Library/IoLib.h>
33 #include <Library/ArmGicLib.h>
35 #include <Protocol/Cpu.h>
36 #include <Protocol/HardwareInterrupt.h>
38 // number of 32-bit registers needed to represent those interrupts as a bit
39 // (used for enable set, enable clear, pending set, pending clear, and active regs)
40 #define ARM_GIC_NUM_REG_PER_INT_BITS (PcdGet32(PcdGicNumInterrupts) / 32)
42 // number of 32-bit registers needed to represent those interrupts as two bits
43 // (used for configuration reg)
44 #define ARM_GIC_NUM_REG_PER_INT_CFG (PcdGet32(PcdGicNumInterrupts) / 16)
46 // number of 32-bit registers needed to represent interrupts as 8-bit priority field
47 // (used for priority regs)
48 #define ARM_GIC_NUM_REG_PER_INT_BYTES (PcdGet32(PcdGicNumInterrupts) / 4)
50 #define ARM_GIC_DEFAULT_PRIORITY 0x80
52 extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol
;
57 EFI_EVENT EfiExitBootServicesEvent
= (EFI_EVENT
)NULL
;
59 HARDWARE_INTERRUPT_HANDLER gRegisteredInterruptHandlers
[FixedPcdGet32(PcdGicNumInterrupts
)];
62 Register Handler for the specified interrupt source.
64 @param This Instance pointer for this protocol
65 @param Source Hardware source of the interrupt
66 @param Handler Callback for interrupt. NULL to unregister
68 @retval EFI_SUCCESS Source was updated to support Handler.
69 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
74 RegisterInterruptSource (
75 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
76 IN HARDWARE_INTERRUPT_SOURCE Source
,
77 IN HARDWARE_INTERRUPT_HANDLER Handler
80 if (Source
> PcdGet32(PcdGicNumInterrupts
)) {
82 return EFI_UNSUPPORTED
;
85 if ((Handler
== NULL
) && (gRegisteredInterruptHandlers
[Source
] == NULL
)) {
86 return EFI_INVALID_PARAMETER
;
89 if ((Handler
!= NULL
) && (gRegisteredInterruptHandlers
[Source
] != NULL
)) {
90 return EFI_ALREADY_STARTED
;
93 gRegisteredInterruptHandlers
[Source
] = Handler
;
95 // If the interrupt handler is unregistered then disable the interrupt
97 return This
->DisableInterruptSource (This
, Source
);
99 return This
->EnableInterruptSource (This
, Source
);
104 Enable interrupt source Source.
106 @param This Instance pointer for this protocol
107 @param Source Hardware source of the interrupt
109 @retval EFI_SUCCESS Source interrupt enabled.
110 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
115 EnableInterruptSource (
116 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
117 IN HARDWARE_INTERRUPT_SOURCE Source
123 if (Source
> PcdGet32(PcdGicNumInterrupts
)) {
125 return EFI_UNSUPPORTED
;
128 // calculate enable register offset and bit position
129 RegOffset
= Source
/ 32;
130 RegShift
= Source
% 32;
132 // write set-enable register
133 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDISER
+ (4*RegOffset
), 1 << RegShift
);
139 Disable interrupt source Source.
141 @param This Instance pointer for this protocol
142 @param Source Hardware source of the interrupt
144 @retval EFI_SUCCESS Source interrupt disabled.
145 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
150 DisableInterruptSource (
151 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
152 IN HARDWARE_INTERRUPT_SOURCE Source
158 if (Source
> PcdGet32(PcdGicNumInterrupts
)) {
160 return EFI_UNSUPPORTED
;
163 // Calculate enable register offset and bit position
164 RegOffset
= Source
/ 32;
165 RegShift
= Source
% 32;
167 // Write set-enable register
168 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDICER
+ (4*RegOffset
), 1 << RegShift
);
174 Return current state of interrupt source Source.
176 @param This Instance pointer for this protocol
177 @param Source Hardware source of the interrupt
178 @param InterruptState TRUE: source enabled, FALSE: source disabled.
180 @retval EFI_SUCCESS InterruptState is valid
181 @retval EFI_DEVICE_ERROR InterruptState is not valid
186 GetInterruptSourceState (
187 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
188 IN HARDWARE_INTERRUPT_SOURCE Source
,
189 IN BOOLEAN
*InterruptState
195 if (Source
> PcdGet32(PcdGicNumInterrupts
)) {
197 return EFI_UNSUPPORTED
;
200 // calculate enable register offset and bit position
201 RegOffset
= Source
/ 32;
202 RegShift
= Source
% 32;
204 if ((MmioRead32 (PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDISER
+ (4*RegOffset
)) & (1<<RegShift
)) == 0) {
205 *InterruptState
= FALSE
;
207 *InterruptState
= TRUE
;
214 Signal to the hardware that the End Of Intrrupt state
217 @param This Instance pointer for this protocol
218 @param Source Hardware source of the interrupt
220 @retval EFI_SUCCESS Source interrupt EOI'ed.
221 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
227 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
228 IN HARDWARE_INTERRUPT_SOURCE Source
231 if (Source
> PcdGet32(PcdGicNumInterrupts
)) {
233 return EFI_UNSUPPORTED
;
236 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCEIOR
, Source
);
241 EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.
243 @param InterruptType Defines the type of interrupt or exception that
244 occurred on the processor.This parameter is processor architecture specific.
245 @param SystemContext A pointer to the processor context when
246 the interrupt occurred on the processor.
253 IrqInterruptHandler (
254 IN EFI_EXCEPTION_TYPE InterruptType
,
255 IN EFI_SYSTEM_CONTEXT SystemContext
259 HARDWARE_INTERRUPT_HANDLER InterruptHandler
;
261 GicInterrupt
= MmioRead32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCIAR
);
263 // Special Interrupts (ID1020-ID1023) have an Interrupt ID greater than the number of interrupt (ie: Spurious interrupt).
264 if (GicInterrupt
>= PcdGet32(PcdGicNumInterrupts
)) {
265 // The special interrupt do not need to be acknowledge
269 InterruptHandler
= gRegisteredInterruptHandlers
[GicInterrupt
];
270 if (InterruptHandler
!= NULL
) {
271 // Call the registered interrupt handler.
272 InterruptHandler (GicInterrupt
, SystemContext
);
274 DEBUG ((EFI_D_ERROR
, "Spurious GIC interrupt: 0x%x\n", GicInterrupt
));
277 EndOfInterrupt (&gHardwareInterruptProtocol
, GicInterrupt
);
281 // Making this global saves a few bytes in image size
283 EFI_HANDLE gHardwareInterruptHandle
= NULL
;
286 // The protocol instance produced by this driver
288 EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol
= {
289 RegisterInterruptSource
,
290 EnableInterruptSource
,
291 DisableInterruptSource
,
292 GetInterruptSourceState
,
297 Shutdown our hardware
299 DXE Core will disable interrupts and turn off the timer and disable interrupts
300 after all the event handlers have run.
302 @param[in] Event The Event that is being processed
303 @param[in] Context Event Context
307 ExitBootServicesEvent (
314 // Acknowledge all pending interrupts
315 for (Index
= 0; Index
< PcdGet32(PcdGicNumInterrupts
); Index
++) {
316 DisableInterruptSource (&gHardwareInterruptProtocol
, Index
);
319 for (Index
= 0; Index
< PcdGet32(PcdGicNumInterrupts
); Index
++) {
320 EndOfInterrupt (&gHardwareInterruptProtocol
, Index
);
323 // Disable Gic Interface
324 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCICR
, 0x0);
325 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCPMR
, 0x0);
327 // Disable Gic Distributor
328 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDDCR
, 0x0);
332 Initialize the state information for the CPU Architectural Protocol
334 @param ImageHandle of the loaded driver
335 @param SystemTable Pointer to the System Table
337 @retval EFI_SUCCESS Protocol registered
338 @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
339 @retval EFI_DEVICE_ERROR Hardware problems
343 InterruptDxeInitialize (
344 IN EFI_HANDLE ImageHandle
,
345 IN EFI_SYSTEM_TABLE
*SystemTable
352 EFI_CPU_ARCH_PROTOCOL
*Cpu
;
354 // Make sure the Interrupt Controller Protocol is not already installed in the system.
355 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL
, &gHardwareInterruptProtocolGuid
);
357 for (Index
= 0; Index
< PcdGet32(PcdGicNumInterrupts
); Index
++) {
358 DisableInterruptSource (&gHardwareInterruptProtocol
, Index
);
361 RegOffset
= Index
/ 4;
362 RegShift
= (Index
% 4) * 8;
364 PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDIPR
+ (4*RegOffset
),
366 ARM_GIC_DEFAULT_PRIORITY
<< RegShift
370 // Configure interrupts for cpu 0
371 for (Index
= 0; Index
< ARM_GIC_NUM_REG_PER_INT_BYTES
; Index
++) {
372 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDIPTR
+ (Index
*4), 0x01010101);
375 // Set binary point reg to 0x7 (no preemption)
376 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCBPR
, 0x7);
378 // Set priority mask reg to 0xff to allow all priorities through
379 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCPMR
, 0xff);
381 // Enable gic cpu interface
382 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCICR
, 0x1);
384 // Enable gic distributor
385 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDDCR
, 0x1);
387 ZeroMem (&gRegisteredInterruptHandlers
, sizeof (gRegisteredInterruptHandlers
));
389 Status
= gBS
->InstallMultipleProtocolInterfaces (
390 &gHardwareInterruptHandle
,
391 &gHardwareInterruptProtocolGuid
, &gHardwareInterruptProtocol
,
394 ASSERT_EFI_ERROR (Status
);
397 // Get the CPU protocol that this driver requires.
399 Status
= gBS
->LocateProtocol(&gEfiCpuArchProtocolGuid
, NULL
, (VOID
**)&Cpu
);
400 ASSERT_EFI_ERROR(Status
);
403 // Unregister the default exception handler.
405 Status
= Cpu
->RegisterInterruptHandler(Cpu
, EXCEPT_ARM_IRQ
, NULL
);
406 ASSERT_EFI_ERROR(Status
);
409 // Register to receive interrupts
411 Status
= Cpu
->RegisterInterruptHandler(Cpu
, EXCEPT_ARM_IRQ
, IrqInterruptHandler
);
412 ASSERT_EFI_ERROR(Status
);
414 // Register for an ExitBootServicesEvent
415 Status
= gBS
->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES
, TPL_NOTIFY
, ExitBootServicesEvent
, NULL
, &EfiExitBootServicesEvent
);
416 ASSERT_EFI_ERROR (Status
);