]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPkg/Drivers/ArmGic/GicV2/ArmGicV2Dxe.c
ArmPkg/ArmGicDxe: Expose HardwareInterrupt2 protocol
[mirror_edk2.git] / ArmPkg / Drivers / ArmGic / GicV2 / ArmGicV2Dxe.c
CommitLineData
f5241b57
OM
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
b0393756 5Portions copyright (c) 2011-2017, ARM Ltd. All rights reserved.<BR>\r
f5241b57
OM
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 GicV2/ArmGicV2Dxe.c\r
18\r
19Abstract:\r
20\r
21 Driver implementing the GicV2 interrupt controller protocol\r
22\r
23--*/\r
24\r
bce29e30
AB
25#include <Library/ArmGicLib.h>\r
26\r
f5241b57 27#include "ArmGicDxe.h"\r
f5241b57
OM
28\r
29#define ARM_GIC_DEFAULT_PRIORITY 0x80\r
30\r
31extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV2Protocol;\r
8659306a 32extern EFI_HARDWARE_INTERRUPT2_PROTOCOL gHardwareInterrupt2V2Protocol;\r
f5241b57 33\r
5f81082e
OM
34STATIC UINT32 mGicInterruptInterfaceBase;\r
35STATIC UINT32 mGicDistributorBase;\r
dc63be24 36\r
f5241b57
OM
37/**\r
38 Enable interrupt source Source.\r
39\r
40 @param This Instance pointer for this protocol\r
41 @param Source Hardware source of the interrupt\r
42\r
43 @retval EFI_SUCCESS Source interrupt enabled.\r
44 @retval EFI_UNSUPPORTED Source interrupt is not supported\r
45\r
46**/\r
b0393756 47STATIC\r
f5241b57
OM
48EFI_STATUS\r
49EFIAPI\r
50GicV2EnableInterruptSource (\r
51 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,\r
52 IN HARDWARE_INTERRUPT_SOURCE Source\r
53 )\r
54{\r
599f004b 55 if (Source >= mGicNumInterrupts) {\r
f5241b57
OM
56 ASSERT(FALSE);\r
57 return EFI_UNSUPPORTED;\r
58 }\r
59\r
41fb5d46 60 ArmGicEnableInterrupt (mGicDistributorBase, 0, Source);\r
f5241b57
OM
61\r
62 return EFI_SUCCESS;\r
63}\r
64\r
65/**\r
66 Disable interrupt source Source.\r
67\r
68 @param This Instance pointer for this protocol\r
69 @param Source Hardware source of the interrupt\r
70\r
71 @retval EFI_SUCCESS Source interrupt disabled.\r
72 @retval EFI_UNSUPPORTED Source interrupt is not supported\r
73\r
74**/\r
b0393756 75STATIC\r
f5241b57
OM
76EFI_STATUS\r
77EFIAPI\r
78GicV2DisableInterruptSource (\r
79 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,\r
80 IN HARDWARE_INTERRUPT_SOURCE Source\r
81 )\r
82{\r
599f004b 83 if (Source >= mGicNumInterrupts) {\r
f5241b57
OM
84 ASSERT(FALSE);\r
85 return EFI_UNSUPPORTED;\r
86 }\r
87\r
41fb5d46 88 ArmGicDisableInterrupt (mGicDistributorBase, 0, Source);\r
f5241b57
OM
89\r
90 return EFI_SUCCESS;\r
91}\r
92\r
93/**\r
94 Return current state of interrupt source Source.\r
95\r
96 @param This Instance pointer for this protocol\r
97 @param Source Hardware source of the interrupt\r
98 @param InterruptState TRUE: source enabled, FALSE: source disabled.\r
99\r
100 @retval EFI_SUCCESS InterruptState is valid\r
101 @retval EFI_UNSUPPORTED Source interrupt is not supported\r
102\r
103**/\r
b0393756 104STATIC\r
f5241b57
OM
105EFI_STATUS\r
106EFIAPI\r
107GicV2GetInterruptSourceState (\r
108 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,\r
109 IN HARDWARE_INTERRUPT_SOURCE Source,\r
110 IN BOOLEAN *InterruptState\r
111 )\r
112{\r
599f004b 113 if (Source >= mGicNumInterrupts) {\r
f5241b57
OM
114 ASSERT(FALSE);\r
115 return EFI_UNSUPPORTED;\r
116 }\r
117\r
41fb5d46 118 *InterruptState = ArmGicIsInterruptEnabled (mGicDistributorBase, 0, Source);\r
f5241b57
OM
119\r
120 return EFI_SUCCESS;\r
121}\r
122\r
123/**\r
124 Signal to the hardware that the End Of Interrupt state\r
125 has been reached.\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 EOI'ed.\r
131 @retval EFI_UNSUPPORTED Source interrupt is not supported\r
132\r
133**/\r
b0393756 134STATIC\r
f5241b57
OM
135EFI_STATUS\r
136EFIAPI\r
137GicV2EndOfInterrupt (\r
138 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,\r
139 IN HARDWARE_INTERRUPT_SOURCE Source\r
140 )\r
141{\r
599f004b 142 if (Source >= mGicNumInterrupts) {\r
f5241b57
OM
143 ASSERT(FALSE);\r
144 return EFI_UNSUPPORTED;\r
145 }\r
146\r
dc63be24 147 ArmGicV2EndOfInterrupt (mGicInterruptInterfaceBase, Source);\r
f5241b57
OM
148 return EFI_SUCCESS;\r
149}\r
150\r
151/**\r
152 EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.\r
153\r
154 @param InterruptType Defines the type of interrupt or exception that\r
b0393756
EL
155 occurred on the processor.This parameter is\r
156 processor architecture specific.\r
f5241b57
OM
157 @param SystemContext A pointer to the processor context when\r
158 the interrupt occurred on the processor.\r
159\r
160 @return None\r
161\r
162**/\r
b0393756 163STATIC\r
f5241b57
OM
164VOID\r
165EFIAPI\r
166GicV2IrqInterruptHandler (\r
167 IN EFI_EXCEPTION_TYPE InterruptType,\r
168 IN EFI_SYSTEM_CONTEXT SystemContext\r
169 )\r
170{\r
171 UINT32 GicInterrupt;\r
172 HARDWARE_INTERRUPT_HANDLER InterruptHandler;\r
173\r
dc63be24 174 GicInterrupt = ArmGicV2AcknowledgeInterrupt (mGicInterruptInterfaceBase);\r
f5241b57 175\r
b0393756
EL
176 // Special Interrupts (ID1020-ID1023) have an Interrupt ID greater than the\r
177 // number of interrupt (ie: Spurious interrupt).\r
f5241b57 178 if ((GicInterrupt & ARM_GIC_ICCIAR_ACKINTID) >= mGicNumInterrupts) {\r
b0393756 179 // The special interrupts do not need to be acknowledged\r
f5241b57
OM
180 return;\r
181 }\r
182\r
183 InterruptHandler = gRegisteredInterruptHandlers[GicInterrupt];\r
184 if (InterruptHandler != NULL) {\r
185 // Call the registered interrupt handler.\r
186 InterruptHandler (GicInterrupt, SystemContext);\r
187 } else {\r
b0393756 188 DEBUG ((DEBUG_ERROR, "Spurious GIC interrupt: 0x%x\n", GicInterrupt));\r
7989300d 189 GicV2EndOfInterrupt (&gHardwareInterruptV2Protocol, GicInterrupt);\r
f5241b57 190 }\r
f5241b57
OM
191}\r
192\r
f5241b57 193// The protocol instance produced by this driver\r
f5241b57
OM
194EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV2Protocol = {\r
195 RegisterInterruptSource,\r
196 GicV2EnableInterruptSource,\r
197 GicV2DisableInterruptSource,\r
198 GicV2GetInterruptSourceState,\r
199 GicV2EndOfInterrupt\r
200};\r
201\r
8659306a
AB
202/**\r
203 Get interrupt trigger type of an interrupt\r
204\r
205 @param This Instance pointer for this protocol\r
206 @param Source Hardware source of the interrupt.\r
207 @param TriggerType Returns interrupt trigger type.\r
208\r
209 @retval EFI_SUCCESS Source interrupt supported.\r
210 @retval EFI_UNSUPPORTED Source interrupt is not supported.\r
211**/\r
212STATIC\r
213EFI_STATUS\r
214EFIAPI\r
215GicV2GetTriggerType (\r
216 IN EFI_HARDWARE_INTERRUPT2_PROTOCOL *This,\r
217 IN HARDWARE_INTERRUPT_SOURCE Source,\r
218 OUT EFI_HARDWARE_INTERRUPT2_TRIGGER_TYPE *TriggerType\r
219 )\r
220{\r
221 UINTN RegAddress;\r
222 UINTN Config1Bit;\r
223 EFI_STATUS Status;\r
224\r
225 Status = GicGetDistributorIcfgBaseAndBit (\r
226 Source,\r
227 &RegAddress,\r
228 &Config1Bit\r
229 );\r
230\r
231 if (EFI_ERROR (Status)) {\r
232 return Status;\r
233 }\r
234\r
235 if ((MmioRead32 (RegAddress) & (1 << Config1Bit)) == 0) {\r
236 *TriggerType = EFI_HARDWARE_INTERRUPT2_TRIGGER_LEVEL_HIGH;\r
237 } else {\r
238 *TriggerType = EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING;\r
239 }\r
240\r
241 return EFI_SUCCESS;\r
242}\r
243\r
244/**\r
245 Set interrupt trigger type of an interrupt\r
246\r
247 @param This Instance pointer for this protocol\r
248 @param Source Hardware source of the interrupt.\r
249 @param TriggerType Interrupt trigger type.\r
250\r
251 @retval EFI_SUCCESS Source interrupt supported.\r
252 @retval EFI_UNSUPPORTED Source interrupt is not supported.\r
253**/\r
254STATIC\r
255EFI_STATUS\r
256EFIAPI\r
257GicV2SetTriggerType (\r
258 IN EFI_HARDWARE_INTERRUPT2_PROTOCOL *This,\r
259 IN HARDWARE_INTERRUPT_SOURCE Source,\r
260 IN EFI_HARDWARE_INTERRUPT2_TRIGGER_TYPE TriggerType\r
261 )\r
262{\r
263 UINTN RegAddress;\r
264 UINTN Config1Bit;\r
265 UINT32 Value;\r
266 EFI_STATUS Status;\r
267 BOOLEAN SourceEnabled;\r
268\r
269 if ( (TriggerType != EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING)\r
270 && (TriggerType != EFI_HARDWARE_INTERRUPT2_TRIGGER_LEVEL_HIGH)) {\r
271 DEBUG ((DEBUG_ERROR, "Invalid interrupt trigger type: %d\n", \\r
272 TriggerType));\r
273 ASSERT (FALSE);\r
274 return EFI_UNSUPPORTED;\r
275 }\r
276\r
277 Status = GicGetDistributorIcfgBaseAndBit (\r
278 Source,\r
279 &RegAddress,\r
280 &Config1Bit\r
281 );\r
282\r
283 if (EFI_ERROR (Status)) {\r
284 return Status;\r
285 }\r
286\r
287 Status = GicV2GetInterruptSourceState (\r
288 (EFI_HARDWARE_INTERRUPT_PROTOCOL*)This,\r
289 Source,\r
290 &SourceEnabled\r
291 );\r
292\r
293 if (EFI_ERROR (Status)) {\r
294 return Status;\r
295 }\r
296\r
297 Value = (TriggerType == EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING)\r
298 ? ARM_GIC_ICDICFR_EDGE_TRIGGERED\r
299 : ARM_GIC_ICDICFR_LEVEL_TRIGGERED;\r
300\r
301 // Before changing the value, we must disable the interrupt,\r
302 // otherwise GIC behavior is UNPREDICTABLE.\r
303 if (SourceEnabled) {\r
304 GicV2DisableInterruptSource (\r
305 (EFI_HARDWARE_INTERRUPT_PROTOCOL*)This,\r
306 Source\r
307 );\r
308 }\r
309\r
310 MmioAndThenOr32 (\r
311 RegAddress,\r
312 ~(0x1 << Config1Bit),\r
313 Value << Config1Bit\r
314 );\r
315\r
316 // Restore interrupt state\r
317 if (SourceEnabled) {\r
318 GicV2EnableInterruptSource (\r
319 (EFI_HARDWARE_INTERRUPT_PROTOCOL*)This,\r
320 Source\r
321 );\r
322 }\r
323\r
324 return EFI_SUCCESS;\r
325}\r
326\r
327EFI_HARDWARE_INTERRUPT2_PROTOCOL gHardwareInterrupt2V2Protocol = {\r
328 (HARDWARE_INTERRUPT2_REGISTER)RegisterInterruptSource,\r
329 (HARDWARE_INTERRUPT2_ENABLE)GicV2EnableInterruptSource,\r
330 (HARDWARE_INTERRUPT2_DISABLE)GicV2DisableInterruptSource,\r
331 (HARDWARE_INTERRUPT2_INTERRUPT_STATE)GicV2GetInterruptSourceState,\r
332 (HARDWARE_INTERRUPT2_END_OF_INTERRUPT)GicV2EndOfInterrupt,\r
333 GicV2GetTriggerType,\r
334 GicV2SetTriggerType\r
335};\r
336\r
f5241b57
OM
337/**\r
338 Shutdown our hardware\r
339\r
b0393756
EL
340 DXE Core will disable interrupts and turn off the timer and disable\r
341 interrupts after all the event handlers have run.\r
f5241b57
OM
342\r
343 @param[in] Event The Event that is being processed\r
344 @param[in] Context Event Context\r
345**/\r
b0393756 346STATIC\r
f5241b57
OM
347VOID\r
348EFIAPI\r
349GicV2ExitBootServicesEvent (\r
350 IN EFI_EVENT Event,\r
351 IN VOID *Context\r
352 )\r
353{\r
354 UINTN Index;\r
355 UINT32 GicInterrupt;\r
356\r
357 // Disable all the interrupts\r
358 for (Index = 0; Index < mGicNumInterrupts; Index++) {\r
359 GicV2DisableInterruptSource (&gHardwareInterruptV2Protocol, Index);\r
360 }\r
361\r
362 // Acknowledge all pending interrupts\r
363 do {\r
dc63be24 364 GicInterrupt = ArmGicV2AcknowledgeInterrupt (mGicInterruptInterfaceBase);\r
f5241b57
OM
365\r
366 if ((GicInterrupt & ARM_GIC_ICCIAR_ACKINTID) < mGicNumInterrupts) {\r
367 GicV2EndOfInterrupt (&gHardwareInterruptV2Protocol, GicInterrupt);\r
368 }\r
369 } while (!ARM_GIC_IS_SPECIAL_INTERRUPTS (GicInterrupt));\r
370\r
371 // Disable Gic Interface\r
dc63be24 372 ArmGicV2DisableInterruptInterface (mGicInterruptInterfaceBase);\r
f5241b57
OM
373\r
374 // Disable Gic Distributor\r
dc63be24 375 ArmGicDisableDistributor (mGicDistributorBase);\r
f5241b57
OM
376}\r
377\r
378/**\r
379 Initialize the state information for the CPU Architectural Protocol\r
380\r
381 @param ImageHandle of the loaded driver\r
382 @param SystemTable Pointer to the System Table\r
383\r
384 @retval EFI_SUCCESS Protocol registered\r
385 @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure\r
386 @retval EFI_DEVICE_ERROR Hardware problems\r
387\r
388**/\r
389EFI_STATUS\r
390GicV2DxeInitialize (\r
391 IN EFI_HANDLE ImageHandle,\r
392 IN EFI_SYSTEM_TABLE *SystemTable\r
393 )\r
394{\r
395 EFI_STATUS Status;\r
396 UINTN Index;\r
397 UINT32 RegOffset;\r
398 UINTN RegShift;\r
399 UINT32 CpuTarget;\r
400\r
b0393756
EL
401 // Make sure the Interrupt Controller Protocol is not already installed in\r
402 // the system.\r
f5241b57
OM
403 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gHardwareInterruptProtocolGuid);\r
404\r
8a1f2378
DC
405 mGicInterruptInterfaceBase = PcdGet64 (PcdGicInterruptInterfaceBase);\r
406 mGicDistributorBase = PcdGet64 (PcdGicDistributorBase);\r
dc63be24 407 mGicNumInterrupts = ArmGicGetMaxNumInterrupts (mGicDistributorBase);\r
f5241b57
OM
408\r
409 for (Index = 0; Index < mGicNumInterrupts; Index++) {\r
410 GicV2DisableInterruptSource (&gHardwareInterruptV2Protocol, Index);\r
411\r
412 // Set Priority\r
413 RegOffset = Index / 4;\r
414 RegShift = (Index % 4) * 8;\r
415 MmioAndThenOr32 (\r
dc63be24 416 mGicDistributorBase + ARM_GIC_ICDIPR + (4 * RegOffset),\r
f5241b57
OM
417 ~(0xff << RegShift),\r
418 ARM_GIC_DEFAULT_PRIORITY << RegShift\r
419 );\r
420 }\r
421\r
f5241b57 422 // Targets the interrupts to the Primary Cpu\r
b0393756
EL
423\r
424 // Only Primary CPU will run this code. We can identify our GIC CPU ID by\r
425 // reading the GIC Distributor Target register. The 8 first GICD_ITARGETSRn\r
426 // are banked to each connected CPU. These 8 registers hold the CPU targets\r
427 // fields for interrupts 0-31. More Info in the GIC Specification about\r
428 // "Interrupt Processor Targets Registers"\r
429\r
430 // Read the first Interrupt Processor Targets Register (that corresponds to\r
431 // the 4 first SGIs)\r
dc63be24 432 CpuTarget = MmioRead32 (mGicDistributorBase + ARM_GIC_ICDIPTR);\r
f5241b57 433\r
b0393756
EL
434 // The CPU target is a bit field mapping each CPU to a GIC CPU Interface.\r
435 // This value is 0 when we run on a uniprocessor platform.\r
f5241b57
OM
436 if (CpuTarget != 0) {\r
437 // The 8 first Interrupt Processor Targets Registers are read-only\r
438 for (Index = 8; Index < (mGicNumInterrupts / 4); Index++) {\r
b0393756
EL
439 MmioWrite32 (\r
440 mGicDistributorBase + ARM_GIC_ICDIPTR + (Index * 4),\r
441 CpuTarget\r
442 );\r
f5241b57
OM
443 }\r
444 }\r
445\r
446 // Set binary point reg to 0x7 (no preemption)\r
dc63be24 447 MmioWrite32 (mGicInterruptInterfaceBase + ARM_GIC_ICCBPR, 0x7);\r
f5241b57
OM
448\r
449 // Set priority mask reg to 0xff to allow all priorities through\r
dc63be24 450 MmioWrite32 (mGicInterruptInterfaceBase + ARM_GIC_ICCPMR, 0xff);\r
f5241b57
OM
451\r
452 // Enable gic cpu interface\r
dc63be24 453 ArmGicEnableInterruptInterface (mGicInterruptInterfaceBase);\r
f5241b57
OM
454\r
455 // Enable gic distributor\r
dc63be24 456 ArmGicEnableDistributor (mGicDistributorBase);\r
f5241b57
OM
457\r
458 Status = InstallAndRegisterInterruptService (\r
b0393756 459 &gHardwareInterruptV2Protocol,\r
8659306a 460 &gHardwareInterrupt2V2Protocol,\r
b0393756
EL
461 GicV2IrqInterruptHandler,\r
462 GicV2ExitBootServicesEvent\r
463 );\r
f5241b57
OM
464\r
465 return Status;\r
466}\r