]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Drivers/PL390Gic/PL390GicDxe.c
ArmPkg: Replaced gArmTokenSpaceGuid.PcdGicNumInterrupts by ArmGicGetMaxNumInterrupts()
[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
347 // Make sure the Interrupt Controller Protocol is not already installed in the system.
348 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gHardwareInterruptProtocolGuid);
349
350 mGicNumInterrupts = ArmGicGetMaxNumInterrupts (PcdGet32(PcdGicDistributorBase));
351
352 for (Index = 0; Index < mGicNumInterrupts; Index++) {
353 DisableInterruptSource (&gHardwareInterruptProtocol, Index);
354
355 // Set Priority
356 RegOffset = Index / 4;
357 RegShift = (Index % 4) * 8;
358 MmioAndThenOr32 (
359 PcdGet32(PcdGicDistributorBase) + ARM_GIC_ICDIPR + (4*RegOffset),
360 ~(0xff << RegShift),
361 ARM_GIC_DEFAULT_PRIORITY << RegShift
362 );
363 }
364
365 // Configure interrupts for cpu 0
366 for (Index = 0; Index < (mGicNumInterrupts / 4); Index++) {
367 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + ARM_GIC_ICDIPTR + (Index*4), 0x01010101);
368 }
369
370 // Set binary point reg to 0x7 (no preemption)
371 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + ARM_GIC_ICCBPR, 0x7);
372
373 // Set priority mask reg to 0xff to allow all priorities through
374 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + ARM_GIC_ICCPMR, 0xff);
375
376 // Enable gic cpu interface
377 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + ARM_GIC_ICCICR, 0x1);
378
379 // Enable gic distributor
380 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + ARM_GIC_ICDDCR, 0x1);
381
382 // Initialize the array for the Interrupt Handlers
383 gRegisteredInterruptHandlers = (HARDWARE_INTERRUPT_HANDLER*)AllocateZeroPool (sizeof(HARDWARE_INTERRUPT_HANDLER) * mGicNumInterrupts);
384
385 Status = gBS->InstallMultipleProtocolInterfaces (
386 &gHardwareInterruptHandle,
387 &gHardwareInterruptProtocolGuid, &gHardwareInterruptProtocol,
388 NULL
389 );
390 ASSERT_EFI_ERROR (Status);
391
392 //
393 // Get the CPU protocol that this driver requires.
394 //
395 Status = gBS->LocateProtocol(&gEfiCpuArchProtocolGuid, NULL, (VOID **)&Cpu);
396 ASSERT_EFI_ERROR(Status);
397
398 //
399 // Unregister the default exception handler.
400 //
401 Status = Cpu->RegisterInterruptHandler(Cpu, EXCEPT_ARM_IRQ, NULL);
402 ASSERT_EFI_ERROR(Status);
403
404 //
405 // Register to receive interrupts
406 //
407 Status = Cpu->RegisterInterruptHandler(Cpu, EXCEPT_ARM_IRQ, IrqInterruptHandler);
408 ASSERT_EFI_ERROR(Status);
409
410 // Register for an ExitBootServicesEvent
411 Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_NOTIFY, ExitBootServicesEvent, NULL, &EfiExitBootServicesEvent);
412 ASSERT_EFI_ERROR (Status);
413
414 return Status;
415 }