]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Drivers/ArmGic/ArmGicDxe.c
ARM Packages: Renamed PL390Gic driver into ArmGic driver
[mirror_edk2.git] / ArmPkg / Drivers / ArmGic / ArmGicDxe.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-2013, 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/ArmLib.h>
28 #include <Library/BaseLib.h>
29 #include <Library/DebugLib.h>
30 #include <Library/BaseMemoryLib.h>
31 #include <Library/MemoryAllocationLib.h>
32 #include <Library/UefiBootServicesTableLib.h>
33 #include <Library/UefiLib.h>
34 #include <Library/PcdLib.h>
35 #include <Library/IoLib.h>
36 #include <Library/ArmGicLib.h>
37
38 #include <Protocol/Cpu.h>
39 #include <Protocol/HardwareInterrupt.h>
40
41 #define ARM_GIC_DEFAULT_PRIORITY 0x80
42
43 extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol;
44
45 //
46 // Notifications
47 //
48 EFI_EVENT EfiExitBootServicesEvent = (EFI_EVENT)NULL;
49
50 // Maximum Number of Interrupts
51 UINTN mGicNumInterrupts = 0;
52
53 HARDWARE_INTERRUPT_HANDLER *gRegisteredInterruptHandlers = NULL;
54
55 /**
56 Register Handler for the specified interrupt source.
57
58 @param This Instance pointer for this protocol
59 @param Source Hardware source of the interrupt
60 @param Handler Callback for interrupt. NULL to unregister
61
62 @retval EFI_SUCCESS Source was updated to support Handler.
63 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
64
65 **/
66 EFI_STATUS
67 EFIAPI
68 RegisterInterruptSource (
69 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
70 IN HARDWARE_INTERRUPT_SOURCE Source,
71 IN HARDWARE_INTERRUPT_HANDLER Handler
72 )
73 {
74 if (Source > mGicNumInterrupts) {
75 ASSERT(FALSE);
76 return EFI_UNSUPPORTED;
77 }
78
79 if ((Handler == NULL) && (gRegisteredInterruptHandlers[Source] == NULL)) {
80 return EFI_INVALID_PARAMETER;
81 }
82
83 if ((Handler != NULL) && (gRegisteredInterruptHandlers[Source] != NULL)) {
84 return EFI_ALREADY_STARTED;
85 }
86
87 gRegisteredInterruptHandlers[Source] = Handler;
88
89 // If the interrupt handler is unregistered then disable the interrupt
90 if (NULL == Handler){
91 return This->DisableInterruptSource (This, Source);
92 } else {
93 return This->EnableInterruptSource (This, Source);
94 }
95 }
96
97 /**
98 Enable interrupt source Source.
99
100 @param This Instance pointer for this protocol
101 @param Source Hardware source of the interrupt
102
103 @retval EFI_SUCCESS Source interrupt enabled.
104 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
105
106 **/
107 EFI_STATUS
108 EFIAPI
109 EnableInterruptSource (
110 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
111 IN HARDWARE_INTERRUPT_SOURCE Source
112 )
113 {
114 UINT32 RegOffset;
115 UINTN RegShift;
116
117 if (Source > mGicNumInterrupts) {
118 ASSERT(FALSE);
119 return EFI_UNSUPPORTED;
120 }
121
122 // Calculate enable register offset and bit position
123 RegOffset = Source / 32;
124 RegShift = Source % 32;
125
126 // Write set-enable register
127 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + ARM_GIC_ICDISER + (4*RegOffset), 1 << RegShift);
128
129 return EFI_SUCCESS;
130 }
131
132 /**
133 Disable interrupt source Source.
134
135 @param This Instance pointer for this protocol
136 @param Source Hardware source of the interrupt
137
138 @retval EFI_SUCCESS Source interrupt disabled.
139 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
140
141 **/
142 EFI_STATUS
143 EFIAPI
144 DisableInterruptSource (
145 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
146 IN HARDWARE_INTERRUPT_SOURCE Source
147 )
148 {
149 UINT32 RegOffset;
150 UINTN RegShift;
151
152 if (Source > mGicNumInterrupts) {
153 ASSERT(FALSE);
154 return EFI_UNSUPPORTED;
155 }
156
157 // Calculate enable register offset and bit position
158 RegOffset = Source / 32;
159 RegShift = Source % 32;
160
161 // Write set-enable register
162 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + ARM_GIC_ICDICER + (4*RegOffset), 1 << RegShift);
163
164 return EFI_SUCCESS;
165 }
166
167 /**
168 Return current state of interrupt source Source.
169
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.
173
174 @retval EFI_SUCCESS InterruptState is valid
175 @retval EFI_DEVICE_ERROR InterruptState is not valid
176
177 **/
178 EFI_STATUS
179 EFIAPI
180 GetInterruptSourceState (
181 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
182 IN HARDWARE_INTERRUPT_SOURCE Source,
183 IN BOOLEAN *InterruptState
184 )
185 {
186 UINT32 RegOffset;
187 UINTN RegShift;
188
189 if (Source > mGicNumInterrupts) {
190 ASSERT(FALSE);
191 return EFI_UNSUPPORTED;
192 }
193
194 // calculate enable register offset and bit position
195 RegOffset = Source / 32;
196 RegShift = Source % 32;
197
198 if ((MmioRead32 (PcdGet32(PcdGicDistributorBase) + ARM_GIC_ICDISER + (4*RegOffset)) & (1<<RegShift)) == 0) {
199 *InterruptState = FALSE;
200 } else {
201 *InterruptState = TRUE;
202 }
203
204 return EFI_SUCCESS;
205 }
206
207 /**
208 Signal to the hardware that the End Of Intrrupt state
209 has been reached.
210
211 @param This Instance pointer for this protocol
212 @param Source Hardware source of the interrupt
213
214 @retval EFI_SUCCESS Source interrupt EOI'ed.
215 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
216
217 **/
218 EFI_STATUS
219 EFIAPI
220 EndOfInterrupt (
221 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
222 IN HARDWARE_INTERRUPT_SOURCE Source
223 )
224 {
225 if (Source > mGicNumInterrupts) {
226 ASSERT(FALSE);
227 return EFI_UNSUPPORTED;
228 }
229
230 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + ARM_GIC_ICCEIOR, Source);
231 return EFI_SUCCESS;
232 }
233
234 /**
235 EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.
236
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.
241
242 @return None
243
244 **/
245 VOID
246 EFIAPI
247 IrqInterruptHandler (
248 IN EFI_EXCEPTION_TYPE InterruptType,
249 IN EFI_SYSTEM_CONTEXT SystemContext
250 )
251 {
252 UINT32 GicInterrupt;
253 HARDWARE_INTERRUPT_HANDLER InterruptHandler;
254
255 GicInterrupt = MmioRead32 (PcdGet32(PcdGicInterruptInterfaceBase) + ARM_GIC_ICCIAR);
256
257 // Special Interrupts (ID1020-ID1023) have an Interrupt ID greater than the number of interrupt (ie: Spurious interrupt).
258 if (GicInterrupt >= mGicNumInterrupts) {
259 // The special interrupt do not need to be acknowledge
260 return;
261 }
262
263 InterruptHandler = gRegisteredInterruptHandlers[GicInterrupt];
264 if (InterruptHandler != NULL) {
265 // Call the registered interrupt handler.
266 InterruptHandler (GicInterrupt, SystemContext);
267 } else {
268 DEBUG ((EFI_D_ERROR, "Spurious GIC interrupt: 0x%x\n", GicInterrupt));
269 }
270
271 EndOfInterrupt (&gHardwareInterruptProtocol, GicInterrupt);
272 }
273
274 //
275 // Making this global saves a few bytes in image size
276 //
277 EFI_HANDLE gHardwareInterruptHandle = NULL;
278
279 //
280 // The protocol instance produced by this driver
281 //
282 EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol = {
283 RegisterInterruptSource,
284 EnableInterruptSource,
285 DisableInterruptSource,
286 GetInterruptSourceState,
287 EndOfInterrupt
288 };
289
290 /**
291 Shutdown our hardware
292
293 DXE Core will disable interrupts and turn off the timer and disable interrupts
294 after all the event handlers have run.
295
296 @param[in] Event The Event that is being processed
297 @param[in] Context Event Context
298 **/
299 VOID
300 EFIAPI
301 ExitBootServicesEvent (
302 IN EFI_EVENT Event,
303 IN VOID *Context
304 )
305 {
306 UINTN Index;
307
308 // Acknowledge all pending interrupts
309 for (Index = 0; Index < mGicNumInterrupts; Index++) {
310 DisableInterruptSource (&gHardwareInterruptProtocol, Index);
311 }
312
313 for (Index = 0; Index < mGicNumInterrupts; Index++) {
314 EndOfInterrupt (&gHardwareInterruptProtocol, Index);
315 }
316
317 // Disable Gic Interface
318 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + ARM_GIC_ICCICR, 0x0);
319 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + ARM_GIC_ICCPMR, 0x0);
320
321 // Disable Gic Distributor
322 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + ARM_GIC_ICDDCR, 0x0);
323 }
324
325 /**
326 Initialize the state information for the CPU Architectural Protocol
327
328 @param ImageHandle of the loaded driver
329 @param SystemTable Pointer to the System Table
330
331 @retval EFI_SUCCESS Protocol registered
332 @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
333 @retval EFI_DEVICE_ERROR Hardware problems
334
335 **/
336 EFI_STATUS
337 InterruptDxeInitialize (
338 IN EFI_HANDLE ImageHandle,
339 IN EFI_SYSTEM_TABLE *SystemTable
340 )
341 {
342 EFI_STATUS Status;
343 UINTN Index;
344 UINT32 RegOffset;
345 UINTN RegShift;
346 EFI_CPU_ARCH_PROTOCOL *Cpu;
347 UINT32 CpuTarget;
348
349 // Make sure the Interrupt Controller Protocol is not already installed in the system.
350 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gHardwareInterruptProtocolGuid);
351
352 mGicNumInterrupts = ArmGicGetMaxNumInterrupts (PcdGet32(PcdGicDistributorBase));
353
354 for (Index = 0; Index < mGicNumInterrupts; Index++) {
355 DisableInterruptSource (&gHardwareInterruptProtocol, Index);
356
357 // Set Priority
358 RegOffset = Index / 4;
359 RegShift = (Index % 4) * 8;
360 MmioAndThenOr32 (
361 PcdGet32(PcdGicDistributorBase) + ARM_GIC_ICDIPR + (4*RegOffset),
362 ~(0xff << RegShift),
363 ARM_GIC_DEFAULT_PRIORITY << RegShift
364 );
365 }
366
367 //
368 // Targets the interrupts to the Primary Cpu
369 //
370
371 // Only Primary CPU will run this code. We can identify our GIC CPU ID by reading
372 // the GIC Distributor Target register. The 8 first GICD_ITARGETSRn are banked to each
373 // connected CPU. These 8 registers hold the CPU targets fields for interrupts 0-31.
374 // More Info in the GIC Specification about "Interrupt Processor Targets Registers"
375 //
376 // Read the first Interrupt Processor Targets Register (that corresponds to the 4
377 // first SGIs)
378 CpuTarget = MmioRead32 (PcdGet32 (PcdGicDistributorBase) + ARM_GIC_ICDIPTR);
379
380 // The CPU target is a bit field mapping each CPU to a GIC CPU Interface. This value
381 // is 0 when we run on a uniprocessor platform.
382 if (CpuTarget != 0) {
383 // The 8 first Interrupt Processor Targets Registers are read-only
384 for (Index = 8; Index < (mGicNumInterrupts / 4); Index++) {
385 MmioWrite32 (PcdGet32 (PcdGicDistributorBase) + ARM_GIC_ICDIPTR + (Index * 4), CpuTarget);
386 }
387 }
388
389 // Set binary point reg to 0x7 (no preemption)
390 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + ARM_GIC_ICCBPR, 0x7);
391
392 // Set priority mask reg to 0xff to allow all priorities through
393 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + ARM_GIC_ICCPMR, 0xff);
394
395 // Enable gic cpu interface
396 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + ARM_GIC_ICCICR, 0x1);
397
398 // Enable gic distributor
399 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + ARM_GIC_ICDDCR, 0x1);
400
401 // Initialize the array for the Interrupt Handlers
402 gRegisteredInterruptHandlers = (HARDWARE_INTERRUPT_HANDLER*)AllocateZeroPool (sizeof(HARDWARE_INTERRUPT_HANDLER) * mGicNumInterrupts);
403
404 Status = gBS->InstallMultipleProtocolInterfaces (
405 &gHardwareInterruptHandle,
406 &gHardwareInterruptProtocolGuid, &gHardwareInterruptProtocol,
407 NULL
408 );
409 ASSERT_EFI_ERROR (Status);
410
411 //
412 // Get the CPU protocol that this driver requires.
413 //
414 Status = gBS->LocateProtocol(&gEfiCpuArchProtocolGuid, NULL, (VOID **)&Cpu);
415 ASSERT_EFI_ERROR(Status);
416
417 //
418 // Unregister the default exception handler.
419 //
420 Status = Cpu->RegisterInterruptHandler(Cpu, ARM_ARCH_EXCEPTION_IRQ, NULL);
421 ASSERT_EFI_ERROR(Status);
422
423 //
424 // Register to receive interrupts
425 //
426 Status = Cpu->RegisterInterruptHandler(Cpu, ARM_ARCH_EXCEPTION_IRQ, IrqInterruptHandler);
427 ASSERT_EFI_ERROR(Status);
428
429 // Register for an ExitBootServicesEvent
430 Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_NOTIFY, ExitBootServicesEvent, NULL, &EfiExitBootServicesEvent);
431 ASSERT_EFI_ERROR (Status);
432
433 return Status;
434 }