]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPkg/Drivers/PL390Gic/PL390GicDxe.c
ArmPkg/LinuxLoader: Fixed memory leak
[mirror_edk2.git] / ArmPkg / Drivers / PL390Gic / PL390GicDxe.c
CommitLineData
1bfda055 1/*++
2
3Copyright (c) 2009, Hewlett-Packard Company. All rights reserved.<BR>
4Portions copyright (c) 2010, Apple Inc. All rights reserved.<BR>
5This program and the accompanying materials
6are licensed and made available under the terms and conditions of the BSD License
7which accompanies this distribution. The full text of the license may be found at
8http://opensource.org/licenses/bsd-license.php
9
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13Module Name:
14
15 Gic.c
16
17Abstract:
18
19 Driver implementing the GIC interrupt controller protocol
20
21--*/
22
23#include <PiDxe.h>
24
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>
55a0d64b 32#include <Library/ArmGicLib.h>
1bfda055 33
34#include <Protocol/Cpu.h>
35#include <Protocol/HardwareInterrupt.h>
36
1bfda055 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)
55a0d64b 39#define ARM_GIC_NUM_REG_PER_INT_BITS (PcdGet32(PcdGicNumInterrupts) / 32)
1bfda055 40
41// number of 32-bit registers needed to represent those interrupts as two bits
42// (used for configuration reg)
55a0d64b 43#define ARM_GIC_NUM_REG_PER_INT_CFG (PcdGet32(PcdGicNumInterrupts) / 16)
1bfda055 44
45// number of 32-bit registers needed to represent interrupts as 8-bit priority field
46// (used for priority regs)
55a0d64b 47#define ARM_GIC_NUM_REG_PER_INT_BYTES (PcdGet32(PcdGicNumInterrupts) / 4)
1bfda055 48
55a0d64b 49#define ARM_GIC_DEFAULT_PRIORITY 0x80
1bfda055 50
51extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol;
52
53//
54// Notifications
55//
1bfda055 56EFI_EVENT EfiExitBootServicesEvent = (EFI_EVENT)NULL;
57
58HARDWARE_INTERRUPT_HANDLER gRegisteredInterruptHandlers[FixedPcdGet32(PcdGicNumInterrupts)];
59
60/**
61 Register Handler for the specified interrupt source.
62
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
66
67 @retval EFI_SUCCESS Source was updated to support Handler.
68 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
69
70**/
71EFI_STATUS
72EFIAPI
73RegisterInterruptSource (
74 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
75 IN HARDWARE_INTERRUPT_SOURCE Source,
76 IN HARDWARE_INTERRUPT_HANDLER Handler
77 )
78{
79 if (Source > PcdGet32(PcdGicNumInterrupts)) {
80 ASSERT(FALSE);
81 return EFI_UNSUPPORTED;
43f69915 82 }
1bfda055 83
84 if ((Handler == NULL) && (gRegisteredInterruptHandlers[Source] == NULL)) {
85 return EFI_INVALID_PARAMETER;
86 }
87
88 if ((Handler != NULL) && (gRegisteredInterruptHandlers[Source] != NULL)) {
89 return EFI_ALREADY_STARTED;
90 }
91
92 gRegisteredInterruptHandlers[Source] = Handler;
43f69915 93
94 // If the interrupt handler is unregistered then disable the interrupt
95 if (NULL == Handler){
96 return This->DisableInterruptSource (This, Source);
97 } else {
98 return This->EnableInterruptSource (This, Source);
99 }
1bfda055 100}
101
102/**
103 Enable interrupt source Source.
104
105 @param This Instance pointer for this protocol
106 @param Source Hardware source of the interrupt
107
108 @retval EFI_SUCCESS Source interrupt enabled.
109 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
110
111**/
112EFI_STATUS
113EFIAPI
114EnableInterruptSource (
115 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
116 IN HARDWARE_INTERRUPT_SOURCE Source
117 )
118{
119 UINT32 RegOffset;
120 UINTN RegShift;
121
122 if (Source > PcdGet32(PcdGicNumInterrupts)) {
123 ASSERT(FALSE);
124 return EFI_UNSUPPORTED;
125 }
126
127 // calculate enable register offset and bit position
128 RegOffset = Source / 32;
129 RegShift = Source % 32;
130
131 // write set-enable register
55a0d64b 132 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + ARM_GIC_ICDISER + (4*RegOffset), 1 << RegShift);
1bfda055 133
134 return EFI_SUCCESS;
135}
136
137/**
138 Disable interrupt source Source.
139
140 @param This Instance pointer for this protocol
141 @param Source Hardware source of the interrupt
142
143 @retval EFI_SUCCESS Source interrupt disabled.
144 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
145
146**/
147EFI_STATUS
148EFIAPI
149DisableInterruptSource (
150 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
151 IN HARDWARE_INTERRUPT_SOURCE Source
152 )
153{
154 UINT32 RegOffset;
155 UINTN RegShift;
156
157 if (Source > PcdGet32(PcdGicNumInterrupts)) {
158 ASSERT(FALSE);
159 return EFI_UNSUPPORTED;
160 }
161
9e2b420e 162 // Calculate enable register offset and bit position
1bfda055 163 RegOffset = Source / 32;
164 RegShift = Source % 32;
165
9e2b420e 166 // Write set-enable register
55a0d64b 167 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + ARM_GIC_ICDICER + (4*RegOffset), 1 << RegShift);
1bfda055 168
169 return EFI_SUCCESS;
170}
171
172/**
173 Return current state of interrupt source Source.
174
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.
178
179 @retval EFI_SUCCESS InterruptState is valid
180 @retval EFI_DEVICE_ERROR InterruptState is not valid
181
182**/
183EFI_STATUS
184EFIAPI
185GetInterruptSourceState (
186 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
187 IN HARDWARE_INTERRUPT_SOURCE Source,
188 IN BOOLEAN *InterruptState
189 )
190{
191 UINT32 RegOffset;
192 UINTN RegShift;
193
194 if (Source > PcdGet32(PcdGicNumInterrupts)) {
195 ASSERT(FALSE);
196 return EFI_UNSUPPORTED;
197 }
198
199 // calculate enable register offset and bit position
200 RegOffset = Source / 32;
201 RegShift = Source % 32;
202
55a0d64b 203 if ((MmioRead32 (PcdGet32(PcdGicDistributorBase) + ARM_GIC_ICDISER + (4*RegOffset)) & (1<<RegShift)) == 0) {
1bfda055 204 *InterruptState = FALSE;
205 } else {
206 *InterruptState = TRUE;
207 }
208
209 return EFI_SUCCESS;
210}
211
212/**
213 Signal to the hardware that the End Of Intrrupt state
214 has been reached.
215
216 @param This Instance pointer for this protocol
217 @param Source Hardware source of the interrupt
218
219 @retval EFI_SUCCESS Source interrupt EOI'ed.
220 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
221
222**/
223EFI_STATUS
224EFIAPI
225EndOfInterrupt (
226 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
227 IN HARDWARE_INTERRUPT_SOURCE Source
228 )
229{
230 if (Source > PcdGet32(PcdGicNumInterrupts)) {
231 ASSERT(FALSE);
232 return EFI_UNSUPPORTED;
233 }
234
55a0d64b 235 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + ARM_GIC_ICCEIOR, Source);
1bfda055 236 return EFI_SUCCESS;
237}
238
239/**
240 EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.
241
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.
246
247 @return None
248
249**/
250VOID
251EFIAPI
252IrqInterruptHandler (
253 IN EFI_EXCEPTION_TYPE InterruptType,
254 IN EFI_SYSTEM_CONTEXT SystemContext
255 )
256{
257 UINT32 GicInterrupt;
258 HARDWARE_INTERRUPT_HANDLER InterruptHandler;
259
55a0d64b 260 GicInterrupt = MmioRead32 (PcdGet32(PcdGicInterruptInterfaceBase) + ARM_GIC_ICCIAR);
261 //TODO: Comment me
1bfda055 262 if (GicInterrupt >= PcdGet32(PcdGicNumInterrupts)) {
55a0d64b 263 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + ARM_GIC_ICCEIOR, GicInterrupt);
fe93eba0 264 return;
1bfda055 265 }
266
267 InterruptHandler = gRegisteredInterruptHandlers[GicInterrupt];
268 if (InterruptHandler != NULL) {
269 // Call the registered interrupt handler.
270 InterruptHandler (GicInterrupt, SystemContext);
271 } else {
272 DEBUG ((EFI_D_ERROR, "Spurious GIC interrupt: 0x%x\n", GicInterrupt));
273 }
274
275 EndOfInterrupt (&gHardwareInterruptProtocol, GicInterrupt);
276}
277
278//
279// Making this global saves a few bytes in image size
280//
281EFI_HANDLE gHardwareInterruptHandle = NULL;
282
283//
284// The protocol instance produced by this driver
285//
286EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol = {
287 RegisterInterruptSource,
288 EnableInterruptSource,
289 DisableInterruptSource,
290 GetInterruptSourceState,
291 EndOfInterrupt
292};
293
294/**
295 Shutdown our hardware
296
297 DXE Core will disable interrupts and turn off the timer and disable interrupts
298 after all the event handlers have run.
299
300 @param[in] Event The Event that is being processed
301 @param[in] Context Event Context
302**/
303VOID
304EFIAPI
305ExitBootServicesEvent (
306 IN EFI_EVENT Event,
307 IN VOID *Context
308 )
309{
d7b6c49b 310 UINTN Index;
1bfda055 311
d7b6c49b 312 for (Index = 0; Index < PcdGet32(PcdGicNumInterrupts); Index++) {
313 DisableInterruptSource (&gHardwareInterruptProtocol, Index);
1bfda055 314 }
315
316 // Acknowledge all pending interrupts
d7b6c49b 317 for (Index = 0; Index < PcdGet32(PcdGicNumInterrupts); Index++) {
318 DisableInterruptSource (&gHardwareInterruptProtocol, Index);
1bfda055 319 }
320
d7b6c49b 321 for (Index = 0; Index < PcdGet32(PcdGicNumInterrupts); Index++) {
322 EndOfInterrupt (&gHardwareInterruptProtocol, Index);
1bfda055 323 }
324
325 // Disable Gic Interface
55a0d64b 326 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + ARM_GIC_ICCICR, 0x0);
327 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + ARM_GIC_ICCPMR, 0x0);
1bfda055 328
329 // Disable Gic Distributor
55a0d64b 330 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + ARM_GIC_ICDDCR, 0x0);
1bfda055 331}
332
1bfda055 333/**
334 Initialize the state information for the CPU Architectural Protocol
335
336 @param ImageHandle of the loaded driver
337 @param SystemTable Pointer to the System Table
338
339 @retval EFI_SUCCESS Protocol registered
340 @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
341 @retval EFI_DEVICE_ERROR Hardware problems
342
343**/
344EFI_STATUS
345InterruptDxeInitialize (
346 IN EFI_HANDLE ImageHandle,
347 IN EFI_SYSTEM_TABLE *SystemTable
348 )
349{
d7b6c49b 350 EFI_STATUS Status;
351 UINTN Index;
352 UINT32 RegOffset;
353 UINTN RegShift;
354 EFI_CPU_ARCH_PROTOCOL *Cpu;
1bfda055 355
356 // Make sure the Interrupt Controller Protocol is not already installed in the system.
357 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gHardwareInterruptProtocolGuid);
358
d7b6c49b 359 for (Index = 0; Index < PcdGet32(PcdGicNumInterrupts); Index++) {
360 DisableInterruptSource (&gHardwareInterruptProtocol, Index);
1bfda055 361
362 // Set Priority
d7b6c49b 363 RegOffset = Index / 4;
364 RegShift = (Index % 4) * 8;
1bfda055 365 MmioAndThenOr32 (
55a0d64b 366 PcdGet32(PcdGicDistributorBase) + ARM_GIC_ICDIPR + (4*RegOffset),
1bfda055 367 ~(0xff << RegShift),
55a0d64b 368 ARM_GIC_DEFAULT_PRIORITY << RegShift
1bfda055 369 );
370 }
371
9e2b420e 372 // Configure interrupts for cpu 0
55a0d64b 373 for (Index = 0; Index < ARM_GIC_NUM_REG_PER_INT_BYTES; Index++) {
374 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + ARM_GIC_ICDIPTR + (Index*4), 0x01010101);
1bfda055 375 }
376
9e2b420e 377 // Set binary point reg to 0x7 (no preemption)
55a0d64b 378 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + ARM_GIC_ICCBPR, 0x7);
1bfda055 379
9e2b420e 380 // Set priority mask reg to 0xff to allow all priorities through
55a0d64b 381 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + ARM_GIC_ICCPMR, 0xff);
1bfda055 382
9e2b420e 383 // Enable gic cpu interface
55a0d64b 384 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + ARM_GIC_ICCICR, 0x1);
1bfda055 385
9e2b420e 386 // Enable gic distributor
55a0d64b 387 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + ARM_GIC_ICDDCR, 0x1);
1bfda055 388
389 ZeroMem (&gRegisteredInterruptHandlers, sizeof (gRegisteredInterruptHandlers));
390
391 Status = gBS->InstallMultipleProtocolInterfaces (
392 &gHardwareInterruptHandle,
393 &gHardwareInterruptProtocolGuid, &gHardwareInterruptProtocol,
394 NULL
395 );
396 ASSERT_EFI_ERROR (Status);
397
d7b6c49b 398 //
399 // Get the CPU protocol that this driver requires.
400 //
401 Status = gBS->LocateProtocol(&gEfiCpuArchProtocolGuid, NULL, (VOID **)&Cpu);
402 ASSERT_EFI_ERROR(Status);
1bfda055 403
d7b6c49b 404 //
405 // Unregister the default exception handler.
406 //
407 Status = Cpu->RegisterInterruptHandler(Cpu, EXCEPT_ARM_IRQ, NULL);
408 ASSERT_EFI_ERROR(Status);
409
410 //
411 // Register to receive interrupts
412 //
413 Status = Cpu->RegisterInterruptHandler(Cpu, EXCEPT_ARM_IRQ, IrqInterruptHandler);
414 ASSERT_EFI_ERROR(Status);
1bfda055 415
416 // Register for an ExitBootServicesEvent
417 Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_NOTIFY, ExitBootServicesEvent, NULL, &EfiExitBootServicesEvent);
418 ASSERT_EFI_ERROR (Status);
419
420 return Status;
421}