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