3 Copyright (c) 2009, Hewlett-Packard Company. All rights reserved.<BR>
4 Portions copyright (c) 2010, Apple Inc. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
19 Driver implementing the GIC interrupt controller protocol
25 #include <Library/BaseLib.h>
26 #include <Library/DebugLib.h>
27 #include <Library/BaseMemoryLib.h>
28 #include <Library/UefiBootServicesTableLib.h>
29 #include <Library/UefiLib.h>
30 #include <Library/PcdLib.h>
31 #include <Library/IoLib.h>
33 #include <Protocol/Cpu.h>
34 #include <Protocol/HardwareInterrupt.h>
36 #include <Drivers/PL390Gic.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 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 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 GIC_NUM_REG_PER_INT_BYTES (PcdGet32(PcdGicNumInterrupts) / 4)
50 #define GIC_DEFAULT_PRIORITY 0x80
52 extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol
;
57 VOID
*CpuProtocolNotificationToken
= NULL
;
58 EFI_EVENT CpuProtocolNotificationEvent
= (EFI_EVENT
)NULL
;
59 EFI_EVENT EfiExitBootServicesEvent
= (EFI_EVENT
)NULL
;
61 HARDWARE_INTERRUPT_HANDLER gRegisteredInterruptHandlers
[FixedPcdGet32(PcdGicNumInterrupts
)];
64 Register Handler for the specified interrupt source.
66 @param This Instance pointer for this protocol
67 @param Source Hardware source of the interrupt
68 @param Handler Callback for interrupt. NULL to unregister
70 @retval EFI_SUCCESS Source was updated to support Handler.
71 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
76 RegisterInterruptSource (
77 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
78 IN HARDWARE_INTERRUPT_SOURCE Source
,
79 IN HARDWARE_INTERRUPT_HANDLER Handler
82 if (Source
> PcdGet32(PcdGicNumInterrupts
)) {
84 return EFI_UNSUPPORTED
;
87 if ((Handler
== NULL
) && (gRegisteredInterruptHandlers
[Source
] == NULL
)) {
88 return EFI_INVALID_PARAMETER
;
91 if ((Handler
!= NULL
) && (gRegisteredInterruptHandlers
[Source
] != NULL
)) {
92 return EFI_ALREADY_STARTED
;
95 gRegisteredInterruptHandlers
[Source
] = Handler
;
96 return This
->EnableInterruptSource(This
, Source
);
100 Enable interrupt source Source.
102 @param This Instance pointer for this protocol
103 @param Source Hardware source of the interrupt
105 @retval EFI_SUCCESS Source interrupt enabled.
106 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
111 EnableInterruptSource (
112 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
113 IN HARDWARE_INTERRUPT_SOURCE Source
119 if (Source
> PcdGet32(PcdGicNumInterrupts
)) {
121 return EFI_UNSUPPORTED
;
124 // calculate enable register offset and bit position
125 RegOffset
= Source
/ 32;
126 RegShift
= Source
% 32;
128 // write set-enable register
129 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + GIC_ICDISER
+(4*RegOffset
), 1 << RegShift
);
135 Disable interrupt source Source.
137 @param This Instance pointer for this protocol
138 @param Source Hardware source of the interrupt
140 @retval EFI_SUCCESS Source interrupt disabled.
141 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
146 DisableInterruptSource (
147 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
148 IN HARDWARE_INTERRUPT_SOURCE Source
154 if (Source
> PcdGet32(PcdGicNumInterrupts
)) {
156 return EFI_UNSUPPORTED
;
159 // calculate enable register offset and bit position
160 RegOffset
= Source
/ 32;
161 RegShift
= Source
% 32;
163 // write set-enable register
164 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + GIC_ICDICER
+(4*RegOffset
), 1 << RegShift
);
170 Return current state of interrupt source Source.
172 @param This Instance pointer for this protocol
173 @param Source Hardware source of the interrupt
174 @param InterruptState TRUE: source enabled, FALSE: source disabled.
176 @retval EFI_SUCCESS InterruptState is valid
177 @retval EFI_DEVICE_ERROR InterruptState is not valid
182 GetInterruptSourceState (
183 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
184 IN HARDWARE_INTERRUPT_SOURCE Source
,
185 IN BOOLEAN
*InterruptState
191 if (Source
> PcdGet32(PcdGicNumInterrupts
)) {
193 return EFI_UNSUPPORTED
;
196 // calculate enable register offset and bit position
197 RegOffset
= Source
/ 32;
198 RegShift
= Source
% 32;
200 if ((MmioRead32 (PcdGet32(PcdGicDistributorBase
) + GIC_ICDISER
+(4*RegOffset
)) & (1<<RegShift
)) == 0) {
201 *InterruptState
= FALSE
;
203 *InterruptState
= TRUE
;
210 Signal to the hardware that the End Of Intrrupt state
213 @param This Instance pointer for this protocol
214 @param Source Hardware source of the interrupt
216 @retval EFI_SUCCESS Source interrupt EOI'ed.
217 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
223 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
224 IN HARDWARE_INTERRUPT_SOURCE Source
227 if (Source
> PcdGet32(PcdGicNumInterrupts
)) {
229 return EFI_UNSUPPORTED
;
232 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCEIOR
, Source
);
237 EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.
239 @param InterruptType Defines the type of interrupt or exception that
240 occurred on the processor.This parameter is processor architecture specific.
241 @param SystemContext A pointer to the processor context when
242 the interrupt occurred on the processor.
249 IrqInterruptHandler (
250 IN EFI_EXCEPTION_TYPE InterruptType
,
251 IN EFI_SYSTEM_CONTEXT SystemContext
255 HARDWARE_INTERRUPT_HANDLER InterruptHandler
;
257 GicInterrupt
= MmioRead32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCIAR
);
258 if (GicInterrupt
>= PcdGet32(PcdGicNumInterrupts
)) {
259 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCEIOR
, GicInterrupt
);
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 for (i
= 0; i
< PcdGet32(PcdGicNumInterrupts
); i
++) {
308 DisableInterruptSource (&gHardwareInterruptProtocol
, i
);
311 // Acknowledge all pending interrupts
312 for (i
= 0; i
< PcdGet32(PcdGicNumInterrupts
); i
++) {
313 DisableInterruptSource (&gHardwareInterruptProtocol
, i
);
316 for (i
= 0; i
< PcdGet32(PcdGicNumInterrupts
); i
++) {
317 EndOfInterrupt (&gHardwareInterruptProtocol
, i
);
320 // Disable Gic Interface
321 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCICR
, 0x0);
322 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCPMR
, 0x0);
324 // Disable Gic Distributor
325 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + GIC_ICDDCR
, 0x0);
329 // Notification routines
332 CpuProtocolInstalledNotification (
338 EFI_CPU_ARCH_PROTOCOL
*Cpu
;
341 // Get the cpu protocol that this driver requires.
343 Status
= gBS
->LocateProtocol(&gEfiCpuArchProtocolGuid
, NULL
, (VOID
**)&Cpu
);
344 ASSERT_EFI_ERROR(Status
);
347 // Unregister the default exception handler.
349 Status
= Cpu
->RegisterInterruptHandler(Cpu
, EXCEPT_ARM_IRQ
, NULL
);
350 ASSERT_EFI_ERROR(Status
);
353 // Register to receive interrupts
355 Status
= Cpu
->RegisterInterruptHandler(Cpu
, EXCEPT_ARM_IRQ
, IrqInterruptHandler
);
356 ASSERT_EFI_ERROR(Status
);
360 Initialize the state information for the CPU Architectural Protocol
362 @param ImageHandle of the loaded driver
363 @param SystemTable Pointer to the System Table
365 @retval EFI_SUCCESS Protocol registered
366 @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
367 @retval EFI_DEVICE_ERROR Hardware problems
371 InterruptDxeInitialize (
372 IN EFI_HANDLE ImageHandle
,
373 IN EFI_SYSTEM_TABLE
*SystemTable
381 // Make sure the Interrupt Controller Protocol is not already installed in the system.
382 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL
, &gHardwareInterruptProtocolGuid
);
384 for (i
= 0; i
< PcdGet32(PcdGicNumInterrupts
); i
++) {
385 DisableInterruptSource (&gHardwareInterruptProtocol
, i
);
389 RegShift
= (i
% 4) * 8;
391 PcdGet32(PcdGicDistributorBase
) + GIC_ICDIPR
+(4*RegOffset
),
393 GIC_DEFAULT_PRIORITY
<< RegShift
397 // configure interrupts for cpu 0
398 for (i
= 0; i
< GIC_NUM_REG_PER_INT_BYTES
; i
++) {
399 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + GIC_ICDIPTR
+ (i
*4), 0x01010101);
402 // set binary point reg to 0x7 (no preemption)
403 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCBPR
, 0x7);
405 // set priority mask reg to 0xff to allow all priorities through
406 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCPMR
, 0xff);
408 // enable gic cpu interface
409 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCICR
, 0x1);
411 // enable gic distributor
412 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + GIC_ICDDCR
, 0x1);
414 ZeroMem (&gRegisteredInterruptHandlers
, sizeof (gRegisteredInterruptHandlers
));
416 Status
= gBS
->InstallMultipleProtocolInterfaces (
417 &gHardwareInterruptHandle
,
418 &gHardwareInterruptProtocolGuid
, &gHardwareInterruptProtocol
,
421 ASSERT_EFI_ERROR (Status
);
423 // Set up to be notified when the Cpu protocol is installed.
424 Status
= gBS
->CreateEvent (EVT_NOTIFY_SIGNAL
, TPL_CALLBACK
, CpuProtocolInstalledNotification
, NULL
, &CpuProtocolNotificationEvent
);
425 ASSERT_EFI_ERROR (Status
);
427 Status
= gBS
->RegisterProtocolNotify (&gEfiCpuArchProtocolGuid
, CpuProtocolNotificationEvent
, (VOID
*)&CpuProtocolNotificationToken
);
428 ASSERT_EFI_ERROR (Status
);
430 // Register for an ExitBootServicesEvent
431 Status
= gBS
->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES
, TPL_NOTIFY
, ExitBootServicesEvent
, NULL
, &EfiExitBootServicesEvent
);
432 ASSERT_EFI_ERROR (Status
);