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
);
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 for (i
= 0; i
< PcdGet32(PcdGicNumInterrupts
); i
++) {
309 DisableInterruptSource (&gHardwareInterruptProtocol
, i
);
312 // Acknowledge all pending interrupts
313 for (i
= 0; i
< PcdGet32(PcdGicNumInterrupts
); i
++) {
314 DisableInterruptSource (&gHardwareInterruptProtocol
, i
);
317 for (i
= 0; i
< PcdGet32(PcdGicNumInterrupts
); i
++) {
318 EndOfInterrupt (&gHardwareInterruptProtocol
, i
);
321 // Disable Gic Interface
322 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCICR
, 0x0);
323 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCPMR
, 0x0);
325 // Disable Gic Distributor
326 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + GIC_ICDDCR
, 0x0);
330 // Notification routines
333 CpuProtocolInstalledNotification (
339 EFI_CPU_ARCH_PROTOCOL
*Cpu
;
342 // Get the cpu protocol that this driver requires.
344 Status
= gBS
->LocateProtocol(&gEfiCpuArchProtocolGuid
, NULL
, (VOID
**)&Cpu
);
345 ASSERT_EFI_ERROR(Status
);
348 // Unregister the default exception handler.
350 Status
= Cpu
->RegisterInterruptHandler(Cpu
, EXCEPT_ARM_IRQ
, NULL
);
351 ASSERT_EFI_ERROR(Status
);
354 // Register to receive interrupts
356 Status
= Cpu
->RegisterInterruptHandler(Cpu
, EXCEPT_ARM_IRQ
, IrqInterruptHandler
);
357 ASSERT_EFI_ERROR(Status
);
361 Initialize the state information for the CPU Architectural Protocol
363 @param ImageHandle of the loaded driver
364 @param SystemTable Pointer to the System Table
366 @retval EFI_SUCCESS Protocol registered
367 @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
368 @retval EFI_DEVICE_ERROR Hardware problems
372 InterruptDxeInitialize (
373 IN EFI_HANDLE ImageHandle
,
374 IN EFI_SYSTEM_TABLE
*SystemTable
382 // Make sure the Interrupt Controller Protocol is not already installed in the system.
383 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL
, &gHardwareInterruptProtocolGuid
);
385 for (i
= 0; i
< PcdGet32(PcdGicNumInterrupts
); i
++) {
386 DisableInterruptSource (&gHardwareInterruptProtocol
, i
);
390 RegShift
= (i
% 4) * 8;
392 PcdGet32(PcdGicDistributorBase
) + GIC_ICDIPR
+ (4*RegOffset
),
394 GIC_DEFAULT_PRIORITY
<< RegShift
398 // Configure interrupts for cpu 0
399 for (i
= 0; i
< GIC_NUM_REG_PER_INT_BYTES
; i
++) {
400 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + GIC_ICDIPTR
+ (i
*4), 0x01010101);
403 // Set binary point reg to 0x7 (no preemption)
404 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCBPR
, 0x7);
406 // Set priority mask reg to 0xff to allow all priorities through
407 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCPMR
, 0xff);
409 // Enable gic cpu interface
410 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCICR
, 0x1);
412 // Enable gic distributor
413 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + GIC_ICDDCR
, 0x1);
415 ZeroMem (&gRegisteredInterruptHandlers
, sizeof (gRegisteredInterruptHandlers
));
417 Status
= gBS
->InstallMultipleProtocolInterfaces (
418 &gHardwareInterruptHandle
,
419 &gHardwareInterruptProtocolGuid
, &gHardwareInterruptProtocol
,
422 ASSERT_EFI_ERROR (Status
);
424 // Set up to be notified when the Cpu protocol is installed.
425 Status
= gBS
->CreateEvent (EVT_NOTIFY_SIGNAL
, TPL_CALLBACK
, CpuProtocolInstalledNotification
, NULL
, &CpuProtocolNotificationEvent
);
426 ASSERT_EFI_ERROR (Status
);
428 Status
= gBS
->RegisterProtocolNotify (&gEfiCpuArchProtocolGuid
, CpuProtocolNotificationEvent
, (VOID
*)&CpuProtocolNotificationToken
);
429 ASSERT_EFI_ERROR (Status
);
431 // Register for an ExitBootServicesEvent
432 Status
= gBS
->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES
, TPL_NOTIFY
, ExitBootServicesEvent
, NULL
, &EfiExitBootServicesEvent
);
433 ASSERT_EFI_ERROR (Status
);