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>
32 #include <Library/ArmGicLib.h>
34 #include <Protocol/Cpu.h>
35 #include <Protocol/HardwareInterrupt.h>
37 // number of 32-bit registers needed to represent those interrupts as a bit
38 // (used for enable set, enable clear, pending set, pending clear, and active regs)
39 #define ARM_GIC_NUM_REG_PER_INT_BITS (PcdGet32(PcdGicNumInterrupts) / 32)
41 // number of 32-bit registers needed to represent those interrupts as two bits
42 // (used for configuration reg)
43 #define ARM_GIC_NUM_REG_PER_INT_CFG (PcdGet32(PcdGicNumInterrupts) / 16)
45 // number of 32-bit registers needed to represent interrupts as 8-bit priority field
46 // (used for priority regs)
47 #define ARM_GIC_NUM_REG_PER_INT_BYTES (PcdGet32(PcdGicNumInterrupts) / 4)
49 #define ARM_GIC_DEFAULT_PRIORITY 0x80
51 extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol
;
56 EFI_EVENT EfiExitBootServicesEvent
= (EFI_EVENT
)NULL
;
58 HARDWARE_INTERRUPT_HANDLER gRegisteredInterruptHandlers
[FixedPcdGet32(PcdGicNumInterrupts
)];
61 Register Handler for the specified interrupt source.
63 @param This Instance pointer for this protocol
64 @param Source Hardware source of the interrupt
65 @param Handler Callback for interrupt. NULL to unregister
67 @retval EFI_SUCCESS Source was updated to support Handler.
68 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
73 RegisterInterruptSource (
74 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
75 IN HARDWARE_INTERRUPT_SOURCE Source
,
76 IN HARDWARE_INTERRUPT_HANDLER Handler
79 if (Source
> PcdGet32(PcdGicNumInterrupts
)) {
81 return EFI_UNSUPPORTED
;
84 if ((Handler
== NULL
) && (gRegisteredInterruptHandlers
[Source
] == NULL
)) {
85 return EFI_INVALID_PARAMETER
;
88 if ((Handler
!= NULL
) && (gRegisteredInterruptHandlers
[Source
] != NULL
)) {
89 return EFI_ALREADY_STARTED
;
92 gRegisteredInterruptHandlers
[Source
] = Handler
;
94 // If the interrupt handler is unregistered then disable the interrupt
96 return This
->DisableInterruptSource (This
, Source
);
98 return This
->EnableInterruptSource (This
, Source
);
103 Enable interrupt source Source.
105 @param This Instance pointer for this protocol
106 @param Source Hardware source of the interrupt
108 @retval EFI_SUCCESS Source interrupt enabled.
109 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
114 EnableInterruptSource (
115 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
116 IN HARDWARE_INTERRUPT_SOURCE Source
122 if (Source
> PcdGet32(PcdGicNumInterrupts
)) {
124 return EFI_UNSUPPORTED
;
127 // calculate enable register offset and bit position
128 RegOffset
= Source
/ 32;
129 RegShift
= Source
% 32;
131 // write set-enable register
132 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDISER
+ (4*RegOffset
), 1 << RegShift
);
138 Disable interrupt source Source.
140 @param This Instance pointer for this protocol
141 @param Source Hardware source of the interrupt
143 @retval EFI_SUCCESS Source interrupt disabled.
144 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
149 DisableInterruptSource (
150 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
151 IN HARDWARE_INTERRUPT_SOURCE Source
157 if (Source
> PcdGet32(PcdGicNumInterrupts
)) {
159 return EFI_UNSUPPORTED
;
162 // Calculate enable register offset and bit position
163 RegOffset
= Source
/ 32;
164 RegShift
= Source
% 32;
166 // Write set-enable register
167 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDICER
+ (4*RegOffset
), 1 << RegShift
);
173 Return current state of interrupt source Source.
175 @param This Instance pointer for this protocol
176 @param Source Hardware source of the interrupt
177 @param InterruptState TRUE: source enabled, FALSE: source disabled.
179 @retval EFI_SUCCESS InterruptState is valid
180 @retval EFI_DEVICE_ERROR InterruptState is not valid
185 GetInterruptSourceState (
186 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
187 IN HARDWARE_INTERRUPT_SOURCE Source
,
188 IN BOOLEAN
*InterruptState
194 if (Source
> PcdGet32(PcdGicNumInterrupts
)) {
196 return EFI_UNSUPPORTED
;
199 // calculate enable register offset and bit position
200 RegOffset
= Source
/ 32;
201 RegShift
= Source
% 32;
203 if ((MmioRead32 (PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDISER
+ (4*RegOffset
)) & (1<<RegShift
)) == 0) {
204 *InterruptState
= FALSE
;
206 *InterruptState
= TRUE
;
213 Signal to the hardware that the End Of Intrrupt state
216 @param This Instance pointer for this protocol
217 @param Source Hardware source of the interrupt
219 @retval EFI_SUCCESS Source interrupt EOI'ed.
220 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
226 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
227 IN HARDWARE_INTERRUPT_SOURCE Source
230 if (Source
> PcdGet32(PcdGicNumInterrupts
)) {
232 return EFI_UNSUPPORTED
;
235 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCEIOR
, Source
);
240 EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.
242 @param InterruptType Defines the type of interrupt or exception that
243 occurred on the processor.This parameter is processor architecture specific.
244 @param SystemContext A pointer to the processor context when
245 the interrupt occurred on the processor.
252 IrqInterruptHandler (
253 IN EFI_EXCEPTION_TYPE InterruptType
,
254 IN EFI_SYSTEM_CONTEXT SystemContext
258 HARDWARE_INTERRUPT_HANDLER InterruptHandler
;
260 GicInterrupt
= MmioRead32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCIAR
);
262 if (GicInterrupt
>= PcdGet32(PcdGicNumInterrupts
)) {
263 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_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
) + ARM_GIC_ICCICR
, 0x0);
327 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCPMR
, 0x0);
329 // Disable Gic Distributor
330 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + ARM_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
) + ARM_GIC_ICDIPR
+ (4*RegOffset
),
368 ARM_GIC_DEFAULT_PRIORITY
<< RegShift
372 // Configure interrupts for cpu 0
373 for (Index
= 0; Index
< ARM_GIC_NUM_REG_PER_INT_BYTES
; Index
++) {
374 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDIPTR
+ (Index
*4), 0x01010101);
377 // Set binary point reg to 0x7 (no preemption)
378 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCBPR
, 0x7);
380 // Set priority mask reg to 0xff to allow all priorities through
381 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCPMR
, 0xff);
383 // Enable gic cpu interface
384 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCICR
, 0x1);
386 // Enable gic distributor
387 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + ARM_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
);