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 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
) + 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
) + 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
) + 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
) + 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
) + GIC_ICCIAR
);
262 if (GicInterrupt
>= PcdGet32(PcdGicNumInterrupts
)) {
263 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCEIOR
, GicInterrupt
);
267 InterruptHandler
= gRegisteredInterruptHandlers
[GicInterrupt
];
268 if (InterruptHandler
!= NULL
) {
269 // Call the registered interrupt handler.
270 InterruptHandler (GicInterrupt
, SystemContext
);
272 DEBUG ((EFI_D_ERROR
, "Spurious GIC interrupt: 0x%x\n", GicInterrupt
));
275 EndOfInterrupt (&gHardwareInterruptProtocol
, GicInterrupt
);
279 // Making this global saves a few bytes in image size
281 EFI_HANDLE gHardwareInterruptHandle
= NULL
;
284 // The protocol instance produced by this driver
286 EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol
= {
287 RegisterInterruptSource
,
288 EnableInterruptSource
,
289 DisableInterruptSource
,
290 GetInterruptSourceState
,
295 Shutdown our hardware
297 DXE Core will disable interrupts and turn off the timer and disable interrupts
298 after all the event handlers have run.
300 @param[in] Event The Event that is being processed
301 @param[in] Context Event Context
305 ExitBootServicesEvent (
312 for (Index
= 0; Index
< PcdGet32(PcdGicNumInterrupts
); Index
++) {
313 DisableInterruptSource (&gHardwareInterruptProtocol
, Index
);
316 // Acknowledge all pending interrupts
317 for (Index
= 0; Index
< PcdGet32(PcdGicNumInterrupts
); Index
++) {
318 DisableInterruptSource (&gHardwareInterruptProtocol
, Index
);
321 for (Index
= 0; Index
< PcdGet32(PcdGicNumInterrupts
); Index
++) {
322 EndOfInterrupt (&gHardwareInterruptProtocol
, Index
);
325 // Disable Gic Interface
326 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCICR
, 0x0);
327 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCPMR
, 0x0);
329 // Disable Gic Distributor
330 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + GIC_ICDDCR
, 0x0);
334 Initialize the state information for the CPU Architectural Protocol
336 @param ImageHandle of the loaded driver
337 @param SystemTable Pointer to the System Table
339 @retval EFI_SUCCESS Protocol registered
340 @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
341 @retval EFI_DEVICE_ERROR Hardware problems
345 InterruptDxeInitialize (
346 IN EFI_HANDLE ImageHandle
,
347 IN EFI_SYSTEM_TABLE
*SystemTable
354 EFI_CPU_ARCH_PROTOCOL
*Cpu
;
356 // Make sure the Interrupt Controller Protocol is not already installed in the system.
357 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL
, &gHardwareInterruptProtocolGuid
);
359 for (Index
= 0; Index
< PcdGet32(PcdGicNumInterrupts
); Index
++) {
360 DisableInterruptSource (&gHardwareInterruptProtocol
, Index
);
363 RegOffset
= Index
/ 4;
364 RegShift
= (Index
% 4) * 8;
366 PcdGet32(PcdGicDistributorBase
) + GIC_ICDIPR
+ (4*RegOffset
),
368 GIC_DEFAULT_PRIORITY
<< RegShift
372 // Configure interrupts for cpu 0
373 for (Index
= 0; Index
< GIC_NUM_REG_PER_INT_BYTES
; Index
++) {
374 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + GIC_ICDIPTR
+ (Index
*4), 0x01010101);
377 // Set binary point reg to 0x7 (no preemption)
378 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCBPR
, 0x7);
380 // Set priority mask reg to 0xff to allow all priorities through
381 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCPMR
, 0xff);
383 // Enable gic cpu interface
384 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCICR
, 0x1);
386 // Enable gic distributor
387 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + GIC_ICDDCR
, 0x1);
389 ZeroMem (&gRegisteredInterruptHandlers
, sizeof (gRegisteredInterruptHandlers
));
391 Status
= gBS
->InstallMultipleProtocolInterfaces (
392 &gHardwareInterruptHandle
,
393 &gHardwareInterruptProtocolGuid
, &gHardwareInterruptProtocol
,
396 ASSERT_EFI_ERROR (Status
);
399 // Get the CPU protocol that this driver requires.
401 Status
= gBS
->LocateProtocol(&gEfiCpuArchProtocolGuid
, NULL
, (VOID
**)&Cpu
);
402 ASSERT_EFI_ERROR(Status
);
405 // Unregister the default exception handler.
407 Status
= Cpu
->RegisterInterruptHandler(Cpu
, EXCEPT_ARM_IRQ
, NULL
);
408 ASSERT_EFI_ERROR(Status
);
411 // Register to receive interrupts
413 Status
= Cpu
->RegisterInterruptHandler(Cpu
, EXCEPT_ARM_IRQ
, IrqInterruptHandler
);
414 ASSERT_EFI_ERROR(Status
);
416 // Register for an ExitBootServicesEvent
417 Status
= gBS
->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES
, TPL_NOTIFY
, ExitBootServicesEvent
, NULL
, &EfiExitBootServicesEvent
);
418 ASSERT_EFI_ERROR (Status
);