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