]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPkg/Drivers/ArmGic/ArmGicDxe.c
ArmPkg/ArmGic: Make the GicDxe driver depends on ArmGicLib (cont)
[mirror_edk2.git] / ArmPkg / Drivers / ArmGic / ArmGicDxe.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
e700a1fc 5Portions copyright (c) 2011-2014, ARM Ltd. All rights reserved.<BR>\r
1e57a462 6\r
e700a1fc
OM
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
1e57a462 14\r
15Module Name:\r
16\r
e700a1fc 17 ArmGicDxe.c\r
1e57a462 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
1e57a462 114 if (Source > mGicNumInterrupts) {\r
115 ASSERT(FALSE);\r
116 return EFI_UNSUPPORTED;\r
117 }\r
1e57a462 118\r
e700a1fc
OM
119 ArmGicEnableInterrupt (FixedPcdGet32 (PcdGicDistributorBase), Source);\r
120\r
1e57a462 121 return EFI_SUCCESS;\r
122}\r
123\r
124/**\r
125 Disable interrupt source Source.\r
126\r
127 @param This Instance pointer for this protocol\r
128 @param Source Hardware source of the interrupt\r
129\r
130 @retval EFI_SUCCESS Source interrupt disabled.\r
131 @retval EFI_DEVICE_ERROR Hardware could not be programmed.\r
132\r
133**/\r
134EFI_STATUS\r
135EFIAPI\r
136DisableInterruptSource (\r
137 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,\r
138 IN HARDWARE_INTERRUPT_SOURCE Source\r
139 )\r
140{\r
1e57a462 141 if (Source > mGicNumInterrupts) {\r
142 ASSERT(FALSE);\r
143 return EFI_UNSUPPORTED;\r
144 }\r
1e57a462 145\r
e700a1fc
OM
146 ArmGicDisableInterrupt (PcdGet32(PcdGicDistributorBase), Source);\r
147\r
1e57a462 148 return EFI_SUCCESS;\r
149}\r
150\r
151/**\r
152 Return current state of interrupt source Source.\r
153\r
154 @param This Instance pointer for this protocol\r
155 @param Source Hardware source of the interrupt\r
156 @param InterruptState TRUE: source enabled, FALSE: source disabled.\r
157\r
158 @retval EFI_SUCCESS InterruptState is valid\r
159 @retval EFI_DEVICE_ERROR InterruptState is not valid\r
160\r
161**/\r
162EFI_STATUS\r
163EFIAPI\r
164GetInterruptSourceState (\r
165 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,\r
166 IN HARDWARE_INTERRUPT_SOURCE Source,\r
167 IN BOOLEAN *InterruptState\r
168 )\r
169{\r
1e57a462 170 if (Source > mGicNumInterrupts) {\r
171 ASSERT(FALSE);\r
172 return EFI_UNSUPPORTED;\r
173 }\r
e700a1fc
OM
174\r
175 *InterruptState = ArmGicIsInterruptEnabled (PcdGet32(PcdGicDistributorBase), Source);\r
176\r
1e57a462 177 return EFI_SUCCESS;\r
178}\r
179\r
180/**\r
181 Signal to the hardware that the End Of Intrrupt state \r
182 has been reached.\r
183\r
184 @param This Instance pointer for this protocol\r
185 @param Source Hardware source of the interrupt\r
186\r
187 @retval EFI_SUCCESS Source interrupt EOI'ed.\r
188 @retval EFI_DEVICE_ERROR Hardware could not be programmed.\r
189\r
190**/\r
191EFI_STATUS\r
192EFIAPI\r
193EndOfInterrupt (\r
194 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,\r
195 IN HARDWARE_INTERRUPT_SOURCE Source\r
196 )\r
197{\r
198 if (Source > mGicNumInterrupts) {\r
199 ASSERT(FALSE);\r
200 return EFI_UNSUPPORTED;\r
201 }\r
202\r
d80401a1 203 ArmGicEndOfInterrupt (PcdGet32(PcdGicInterruptInterfaceBase), Source);\r
1e57a462 204 return EFI_SUCCESS;\r
205}\r
206\r
207/**\r
208 EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.\r
209\r
210 @param InterruptType Defines the type of interrupt or exception that\r
211 occurred on the processor.This parameter is processor architecture specific.\r
212 @param SystemContext A pointer to the processor context when\r
213 the interrupt occurred on the processor.\r
214\r
215 @return None\r
216\r
217**/\r
218VOID\r
219EFIAPI\r
220IrqInterruptHandler (\r
221 IN EFI_EXCEPTION_TYPE InterruptType,\r
222 IN EFI_SYSTEM_CONTEXT SystemContext\r
223 )\r
224{\r
225 UINT32 GicInterrupt;\r
226 HARDWARE_INTERRUPT_HANDLER InterruptHandler;\r
227\r
2ca815a4 228 GicInterrupt = ArmGicAcknowledgeInterrupt (PcdGet32(PcdGicInterruptInterfaceBase));\r
1e57a462 229\r
230 // Special Interrupts (ID1020-ID1023) have an Interrupt ID greater than the number of interrupt (ie: Spurious interrupt).\r
2ca815a4 231 if ((GicInterrupt & ARM_GIC_ICCIAR_ACKINTID) >= mGicNumInterrupts) {\r
1e57a462 232 // The special interrupt do not need to be acknowledge\r
233 return;\r
234 }\r
235 \r
236 InterruptHandler = gRegisteredInterruptHandlers[GicInterrupt];\r
237 if (InterruptHandler != NULL) {\r
238 // Call the registered interrupt handler.\r
239 InterruptHandler (GicInterrupt, SystemContext);\r
240 } else {\r
241 DEBUG ((EFI_D_ERROR, "Spurious GIC interrupt: 0x%x\n", GicInterrupt));\r
242 }\r
243\r
244 EndOfInterrupt (&gHardwareInterruptProtocol, GicInterrupt);\r
245}\r
246\r
247//\r
248// Making this global saves a few bytes in image size\r
249//\r
250EFI_HANDLE gHardwareInterruptHandle = NULL;\r
251\r
252//\r
253// The protocol instance produced by this driver\r
254//\r
255EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol = {\r
256 RegisterInterruptSource,\r
257 EnableInterruptSource,\r
258 DisableInterruptSource,\r
259 GetInterruptSourceState,\r
260 EndOfInterrupt\r
261};\r
262\r
263/**\r
264 Shutdown our hardware\r
265 \r
266 DXE Core will disable interrupts and turn off the timer and disable interrupts\r
267 after all the event handlers have run.\r
268\r
269 @param[in] Event The Event that is being processed\r
270 @param[in] Context Event Context\r
271**/\r
272VOID\r
273EFIAPI\r
274ExitBootServicesEvent (\r
275 IN EFI_EVENT Event,\r
276 IN VOID *Context\r
277 )\r
278{\r
279 UINTN Index;\r
280 \r
281 // Acknowledge all pending interrupts\r
282 for (Index = 0; Index < mGicNumInterrupts; Index++) {\r
283 DisableInterruptSource (&gHardwareInterruptProtocol, Index);\r
284 }\r
285\r
286 for (Index = 0; Index < mGicNumInterrupts; Index++) {\r
287 EndOfInterrupt (&gHardwareInterruptProtocol, Index);\r
288 }\r
289\r
290 // Disable Gic Interface\r
e700a1fc 291 ArmGicDisableInterruptInterface (PcdGet32(PcdGicInterruptInterfaceBase));\r
1e57a462 292\r
293 // Disable Gic Distributor\r
e700a1fc 294 ArmGicDisableDistributor (PcdGet32(PcdGicDistributorBase));\r
1e57a462 295}\r
296\r
297/**\r
298 Initialize the state information for the CPU Architectural Protocol\r
299\r
300 @param ImageHandle of the loaded driver\r
301 @param SystemTable Pointer to the System Table\r
302\r
303 @retval EFI_SUCCESS Protocol registered\r
304 @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure\r
305 @retval EFI_DEVICE_ERROR Hardware problems\r
306\r
307**/\r
308EFI_STATUS\r
309InterruptDxeInitialize (\r
310 IN EFI_HANDLE ImageHandle,\r
311 IN EFI_SYSTEM_TABLE *SystemTable\r
312 )\r
313{\r
314 EFI_STATUS Status;\r
315 UINTN Index;\r
316 UINT32 RegOffset;\r
317 UINTN RegShift;\r
318 EFI_CPU_ARCH_PROTOCOL *Cpu;\r
319 UINT32 CpuTarget;\r
320 \r
1e57a462 321 // Make sure the Interrupt Controller Protocol is not already installed in the system.\r
322 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gHardwareInterruptProtocolGuid);\r
323\r
324 mGicNumInterrupts = ArmGicGetMaxNumInterrupts (PcdGet32(PcdGicDistributorBase));\r
325\r
326 for (Index = 0; Index < mGicNumInterrupts; Index++) {\r
327 DisableInterruptSource (&gHardwareInterruptProtocol, Index);\r
328 \r
329 // Set Priority \r
330 RegOffset = Index / 4;\r
331 RegShift = (Index % 4) * 8;\r
332 MmioAndThenOr32 (\r
333 PcdGet32(PcdGicDistributorBase) + ARM_GIC_ICDIPR + (4*RegOffset),\r
334 ~(0xff << RegShift), \r
335 ARM_GIC_DEFAULT_PRIORITY << RegShift\r
336 );\r
337 }\r
338\r
a1cca638
OM
339 //\r
340 // Targets the interrupts to the Primary Cpu\r
341 //\r
342\r
343 // Only Primary CPU will run this code. We can identify our GIC CPU ID by reading\r
344 // the GIC Distributor Target register. The 8 first GICD_ITARGETSRn are banked to each\r
345 // connected CPU. These 8 registers hold the CPU targets fields for interrupts 0-31.\r
346 // More Info in the GIC Specification about "Interrupt Processor Targets Registers"\r
347 //\r
348 // Read the first Interrupt Processor Targets Register (that corresponds to the 4\r
349 // first SGIs)\r
350 CpuTarget = MmioRead32 (PcdGet32 (PcdGicDistributorBase) + ARM_GIC_ICDIPTR);\r
351\r
352 // The CPU target is a bit field mapping each CPU to a GIC CPU Interface. This value\r
eb5c268f
OM
353 // is 0 when we run on a uniprocessor platform.\r
354 if (CpuTarget != 0) {\r
355 // The 8 first Interrupt Processor Targets Registers are read-only\r
356 for (Index = 8; Index < (mGicNumInterrupts / 4); Index++) {\r
357 MmioWrite32 (PcdGet32 (PcdGicDistributorBase) + ARM_GIC_ICDIPTR + (Index * 4), CpuTarget);\r
358 }\r
1e57a462 359 }\r
360\r
361 // Set binary point reg to 0x7 (no preemption)\r
362 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + ARM_GIC_ICCBPR, 0x7);\r
363\r
364 // Set priority mask reg to 0xff to allow all priorities through\r
365 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + ARM_GIC_ICCPMR, 0xff);\r
e700a1fc 366\r
1e57a462 367 // Enable gic cpu interface\r
e700a1fc 368 ArmGicEnableInterruptInterface (PcdGet32(PcdGicInterruptInterfaceBase));\r
1e57a462 369\r
370 // Enable gic distributor\r
e700a1fc
OM
371 ArmGicEnableDistributor (PcdGet32(PcdGicDistributorBase));\r
372\r
1e57a462 373 // Initialize the array for the Interrupt Handlers\r
374 gRegisteredInterruptHandlers = (HARDWARE_INTERRUPT_HANDLER*)AllocateZeroPool (sizeof(HARDWARE_INTERRUPT_HANDLER) * mGicNumInterrupts);\r
e700a1fc 375\r
1e57a462 376 Status = gBS->InstallMultipleProtocolInterfaces (\r
377 &gHardwareInterruptHandle,\r
378 &gHardwareInterruptProtocolGuid, &gHardwareInterruptProtocol,\r
379 NULL\r
380 );\r
381 ASSERT_EFI_ERROR (Status);\r
e700a1fc 382\r
1e57a462 383 //\r
384 // Get the CPU protocol that this driver requires.\r
385 //\r
386 Status = gBS->LocateProtocol(&gEfiCpuArchProtocolGuid, NULL, (VOID **)&Cpu);\r
387 ASSERT_EFI_ERROR(Status);\r
388\r
389 //\r
390 // Unregister the default exception handler.\r
391 //\r
111339d2 392 Status = Cpu->RegisterInterruptHandler(Cpu, ARM_ARCH_EXCEPTION_IRQ, NULL);\r
1e57a462 393 ASSERT_EFI_ERROR(Status);\r
394\r
395 //\r
396 // Register to receive interrupts\r
397 //\r
111339d2 398 Status = Cpu->RegisterInterruptHandler(Cpu, ARM_ARCH_EXCEPTION_IRQ, IrqInterruptHandler);\r
1e57a462 399 ASSERT_EFI_ERROR(Status);\r
400\r
401 // Register for an ExitBootServicesEvent\r
402 Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_NOTIFY, ExitBootServicesEvent, NULL, &EfiExitBootServicesEvent);\r
403 ASSERT_EFI_ERROR (Status);\r
404\r
405 return Status;\r
406}\r