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
;
94 return This
->EnableInterruptSource(This
, Source
);
98 Enable interrupt source Source.
100 @param This Instance pointer for this protocol
101 @param Source Hardware source of the interrupt
103 @retval EFI_SUCCESS Source interrupt enabled.
104 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
109 EnableInterruptSource (
110 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
111 IN HARDWARE_INTERRUPT_SOURCE Source
117 if (Source
> PcdGet32(PcdGicNumInterrupts
)) {
119 return EFI_UNSUPPORTED
;
122 // calculate enable register offset and bit position
123 RegOffset
= Source
/ 32;
124 RegShift
= Source
% 32;
126 // write set-enable register
127 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + GIC_ICDISER
+ (4*RegOffset
), 1 << RegShift
);
133 Disable interrupt source Source.
135 @param This Instance pointer for this protocol
136 @param Source Hardware source of the interrupt
138 @retval EFI_SUCCESS Source interrupt disabled.
139 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
144 DisableInterruptSource (
145 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
146 IN HARDWARE_INTERRUPT_SOURCE Source
152 if (Source
> PcdGet32(PcdGicNumInterrupts
)) {
154 return EFI_UNSUPPORTED
;
157 // Calculate enable register offset and bit position
158 RegOffset
= Source
/ 32;
159 RegShift
= Source
% 32;
161 // Write set-enable register
162 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + GIC_ICDICER
+ (4*RegOffset
), 1 << RegShift
);
168 Return current state of interrupt source Source.
170 @param This Instance pointer for this protocol
171 @param Source Hardware source of the interrupt
172 @param InterruptState TRUE: source enabled, FALSE: source disabled.
174 @retval EFI_SUCCESS InterruptState is valid
175 @retval EFI_DEVICE_ERROR InterruptState is not valid
180 GetInterruptSourceState (
181 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
182 IN HARDWARE_INTERRUPT_SOURCE Source
,
183 IN BOOLEAN
*InterruptState
189 if (Source
> PcdGet32(PcdGicNumInterrupts
)) {
191 return EFI_UNSUPPORTED
;
194 // calculate enable register offset and bit position
195 RegOffset
= Source
/ 32;
196 RegShift
= Source
% 32;
198 if ((MmioRead32 (PcdGet32(PcdGicDistributorBase
) + GIC_ICDISER
+ (4*RegOffset
)) & (1<<RegShift
)) == 0) {
199 *InterruptState
= FALSE
;
201 *InterruptState
= TRUE
;
208 Signal to the hardware that the End Of Intrrupt state
211 @param This Instance pointer for this protocol
212 @param Source Hardware source of the interrupt
214 @retval EFI_SUCCESS Source interrupt EOI'ed.
215 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
221 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
222 IN HARDWARE_INTERRUPT_SOURCE Source
225 if (Source
> PcdGet32(PcdGicNumInterrupts
)) {
227 return EFI_UNSUPPORTED
;
230 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCEIOR
, Source
);
235 EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.
237 @param InterruptType Defines the type of interrupt or exception that
238 occurred on the processor.This parameter is processor architecture specific.
239 @param SystemContext A pointer to the processor context when
240 the interrupt occurred on the processor.
247 IrqInterruptHandler (
248 IN EFI_EXCEPTION_TYPE InterruptType
,
249 IN EFI_SYSTEM_CONTEXT SystemContext
253 HARDWARE_INTERRUPT_HANDLER InterruptHandler
;
255 GicInterrupt
= MmioRead32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCIAR
);
256 if (GicInterrupt
>= PcdGet32(PcdGicNumInterrupts
)) {
257 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCEIOR
, GicInterrupt
);
261 InterruptHandler
= gRegisteredInterruptHandlers
[GicInterrupt
];
262 if (InterruptHandler
!= NULL
) {
263 // Call the registered interrupt handler.
264 InterruptHandler (GicInterrupt
, SystemContext
);
266 DEBUG ((EFI_D_ERROR
, "Spurious GIC interrupt: 0x%x\n", GicInterrupt
));
269 EndOfInterrupt (&gHardwareInterruptProtocol
, GicInterrupt
);
273 // Making this global saves a few bytes in image size
275 EFI_HANDLE gHardwareInterruptHandle
= NULL
;
278 // The protocol instance produced by this driver
280 EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol
= {
281 RegisterInterruptSource
,
282 EnableInterruptSource
,
283 DisableInterruptSource
,
284 GetInterruptSourceState
,
289 Shutdown our hardware
291 DXE Core will disable interrupts and turn off the timer and disable interrupts
292 after all the event handlers have run.
294 @param[in] Event The Event that is being processed
295 @param[in] Context Event Context
299 ExitBootServicesEvent (
306 for (Index
= 0; Index
< PcdGet32(PcdGicNumInterrupts
); Index
++) {
307 DisableInterruptSource (&gHardwareInterruptProtocol
, Index
);
310 // Acknowledge all pending interrupts
311 for (Index
= 0; Index
< PcdGet32(PcdGicNumInterrupts
); Index
++) {
312 DisableInterruptSource (&gHardwareInterruptProtocol
, Index
);
315 for (Index
= 0; Index
< PcdGet32(PcdGicNumInterrupts
); Index
++) {
316 EndOfInterrupt (&gHardwareInterruptProtocol
, Index
);
319 // Disable Gic Interface
320 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCICR
, 0x0);
321 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCPMR
, 0x0);
323 // Disable Gic Distributor
324 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + GIC_ICDDCR
, 0x0);
328 Initialize the state information for the CPU Architectural Protocol
330 @param ImageHandle of the loaded driver
331 @param SystemTable Pointer to the System Table
333 @retval EFI_SUCCESS Protocol registered
334 @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
335 @retval EFI_DEVICE_ERROR Hardware problems
339 InterruptDxeInitialize (
340 IN EFI_HANDLE ImageHandle
,
341 IN EFI_SYSTEM_TABLE
*SystemTable
348 EFI_CPU_ARCH_PROTOCOL
*Cpu
;
350 // Make sure the Interrupt Controller Protocol is not already installed in the system.
351 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL
, &gHardwareInterruptProtocolGuid
);
353 for (Index
= 0; Index
< PcdGet32(PcdGicNumInterrupts
); Index
++) {
354 DisableInterruptSource (&gHardwareInterruptProtocol
, Index
);
357 RegOffset
= Index
/ 4;
358 RegShift
= (Index
% 4) * 8;
360 PcdGet32(PcdGicDistributorBase
) + GIC_ICDIPR
+ (4*RegOffset
),
362 GIC_DEFAULT_PRIORITY
<< RegShift
366 // Configure interrupts for cpu 0
367 for (Index
= 0; Index
< GIC_NUM_REG_PER_INT_BYTES
; Index
++) {
368 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + GIC_ICDIPTR
+ (Index
*4), 0x01010101);
371 // Set binary point reg to 0x7 (no preemption)
372 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCBPR
, 0x7);
374 // Set priority mask reg to 0xff to allow all priorities through
375 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCPMR
, 0xff);
377 // Enable gic cpu interface
378 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + GIC_ICCICR
, 0x1);
380 // Enable gic distributor
381 MmioWrite32 (PcdGet32(PcdGicDistributorBase
) + GIC_ICDDCR
, 0x1);
383 ZeroMem (&gRegisteredInterruptHandlers
, sizeof (gRegisteredInterruptHandlers
));
385 Status
= gBS
->InstallMultipleProtocolInterfaces (
386 &gHardwareInterruptHandle
,
387 &gHardwareInterruptProtocolGuid
, &gHardwareInterruptProtocol
,
390 ASSERT_EFI_ERROR (Status
);
393 // Get the CPU protocol that this driver requires.
395 Status
= gBS
->LocateProtocol(&gEfiCpuArchProtocolGuid
, NULL
, (VOID
**)&Cpu
);
396 ASSERT_EFI_ERROR(Status
);
399 // Unregister the default exception handler.
401 Status
= Cpu
->RegisterInterruptHandler(Cpu
, EXCEPT_ARM_IRQ
, NULL
);
402 ASSERT_EFI_ERROR(Status
);
405 // Register to receive interrupts
407 Status
= Cpu
->RegisterInterruptHandler(Cpu
, EXCEPT_ARM_IRQ
, IrqInterruptHandler
);
408 ASSERT_EFI_ERROR(Status
);
410 // Register for an ExitBootServicesEvent
411 Status
= gBS
->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES
, TPL_NOTIFY
, ExitBootServicesEvent
, NULL
, &EfiExitBootServicesEvent
);
412 ASSERT_EFI_ERROR (Status
);