]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPkg/Drivers/ArmGic/ArmGicDxe.c
ArmPkg/ArmGic: Move the installation and the registration to InstallAndRegisterInterr...
[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
27#include <Library/BaseLib.h>\r
1e57a462 28#include <Library/BaseMemoryLib.h>\r
1e57a462 29#include <Library/UefiLib.h>\r
30#include <Library/PcdLib.h>\r
31#include <Library/IoLib.h>\r
32#include <Library/ArmGicLib.h>\r
33\r
69b5dc9f 34#include "ArmGicDxe.h"\r
1e57a462 35\r
36#define ARM_GIC_DEFAULT_PRIORITY 0x80\r
37\r
38extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol;\r
39\r
1e57a462 40/**\r
41 Register Handler for the specified interrupt source.\r
42\r
43 @param This Instance pointer for this protocol\r
44 @param Source Hardware source of the interrupt\r
45 @param Handler Callback for interrupt. NULL to unregister\r
46\r
47 @retval EFI_SUCCESS Source was updated to support Handler.\r
48 @retval EFI_DEVICE_ERROR Hardware could not be programmed.\r
49\r
50**/\r
51EFI_STATUS\r
52EFIAPI\r
53RegisterInterruptSource (\r
54 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,\r
55 IN HARDWARE_INTERRUPT_SOURCE Source,\r
56 IN HARDWARE_INTERRUPT_HANDLER Handler\r
57 )\r
58{\r
59 if (Source > mGicNumInterrupts) {\r
60 ASSERT(FALSE);\r
61 return EFI_UNSUPPORTED;\r
62 }\r
63 \r
64 if ((Handler == NULL) && (gRegisteredInterruptHandlers[Source] == NULL)) {\r
65 return EFI_INVALID_PARAMETER;\r
66 }\r
67\r
68 if ((Handler != NULL) && (gRegisteredInterruptHandlers[Source] != NULL)) {\r
69 return EFI_ALREADY_STARTED;\r
70 }\r
71\r
72 gRegisteredInterruptHandlers[Source] = Handler;\r
73\r
74 // If the interrupt handler is unregistered then disable the interrupt\r
75 if (NULL == Handler){\r
76 return This->DisableInterruptSource (This, Source);\r
77 } else {\r
78 return This->EnableInterruptSource (This, Source);\r
79 }\r
80}\r
81\r
82/**\r
83 Enable interrupt source Source.\r
84\r
85 @param This Instance pointer for this protocol\r
86 @param Source Hardware source of the interrupt\r
87\r
88 @retval EFI_SUCCESS Source interrupt enabled.\r
89 @retval EFI_DEVICE_ERROR Hardware could not be programmed.\r
90\r
91**/\r
92EFI_STATUS\r
93EFIAPI\r
94EnableInterruptSource (\r
95 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,\r
96 IN HARDWARE_INTERRUPT_SOURCE Source\r
97 )\r
98{\r
1e57a462 99 if (Source > mGicNumInterrupts) {\r
100 ASSERT(FALSE);\r
101 return EFI_UNSUPPORTED;\r
102 }\r
1e57a462 103\r
e700a1fc
OM
104 ArmGicEnableInterrupt (FixedPcdGet32 (PcdGicDistributorBase), Source);\r
105\r
1e57a462 106 return EFI_SUCCESS;\r
107}\r
108\r
109/**\r
110 Disable interrupt source Source.\r
111\r
112 @param This Instance pointer for this protocol\r
113 @param Source Hardware source of the interrupt\r
114\r
115 @retval EFI_SUCCESS Source interrupt disabled.\r
116 @retval EFI_DEVICE_ERROR Hardware could not be programmed.\r
117\r
118**/\r
119EFI_STATUS\r
120EFIAPI\r
121DisableInterruptSource (\r
122 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,\r
123 IN HARDWARE_INTERRUPT_SOURCE Source\r
124 )\r
125{\r
1e57a462 126 if (Source > mGicNumInterrupts) {\r
127 ASSERT(FALSE);\r
128 return EFI_UNSUPPORTED;\r
129 }\r
1e57a462 130\r
e700a1fc
OM
131 ArmGicDisableInterrupt (PcdGet32(PcdGicDistributorBase), Source);\r
132\r
1e57a462 133 return EFI_SUCCESS;\r
134}\r
135\r
136/**\r
137 Return current state of interrupt source Source.\r
138\r
139 @param This Instance pointer for this protocol\r
140 @param Source Hardware source of the interrupt\r
141 @param InterruptState TRUE: source enabled, FALSE: source disabled.\r
142\r
143 @retval EFI_SUCCESS InterruptState is valid\r
144 @retval EFI_DEVICE_ERROR InterruptState is not valid\r
145\r
146**/\r
147EFI_STATUS\r
148EFIAPI\r
149GetInterruptSourceState (\r
150 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,\r
151 IN HARDWARE_INTERRUPT_SOURCE Source,\r
152 IN BOOLEAN *InterruptState\r
153 )\r
154{\r
1e57a462 155 if (Source > mGicNumInterrupts) {\r
156 ASSERT(FALSE);\r
157 return EFI_UNSUPPORTED;\r
158 }\r
e700a1fc
OM
159\r
160 *InterruptState = ArmGicIsInterruptEnabled (PcdGet32(PcdGicDistributorBase), Source);\r
161\r
1e57a462 162 return EFI_SUCCESS;\r
163}\r
164\r
165/**\r
166 Signal to the hardware that the End Of Intrrupt state \r
167 has been reached.\r
168\r
169 @param This Instance pointer for this protocol\r
170 @param Source Hardware source of the interrupt\r
171\r
172 @retval EFI_SUCCESS Source interrupt EOI'ed.\r
173 @retval EFI_DEVICE_ERROR Hardware could not be programmed.\r
174\r
175**/\r
176EFI_STATUS\r
177EFIAPI\r
178EndOfInterrupt (\r
179 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,\r
180 IN HARDWARE_INTERRUPT_SOURCE Source\r
181 )\r
182{\r
183 if (Source > mGicNumInterrupts) {\r
184 ASSERT(FALSE);\r
185 return EFI_UNSUPPORTED;\r
186 }\r
187\r
d80401a1 188 ArmGicEndOfInterrupt (PcdGet32(PcdGicInterruptInterfaceBase), Source);\r
1e57a462 189 return EFI_SUCCESS;\r
190}\r
191\r
192/**\r
193 EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.\r
194\r
195 @param InterruptType Defines the type of interrupt or exception that\r
196 occurred on the processor.This parameter is processor architecture specific.\r
197 @param SystemContext A pointer to the processor context when\r
198 the interrupt occurred on the processor.\r
199\r
200 @return None\r
201\r
202**/\r
203VOID\r
204EFIAPI\r
205IrqInterruptHandler (\r
206 IN EFI_EXCEPTION_TYPE InterruptType,\r
207 IN EFI_SYSTEM_CONTEXT SystemContext\r
208 )\r
209{\r
210 UINT32 GicInterrupt;\r
211 HARDWARE_INTERRUPT_HANDLER InterruptHandler;\r
212\r
2ca815a4 213 GicInterrupt = ArmGicAcknowledgeInterrupt (PcdGet32(PcdGicInterruptInterfaceBase));\r
1e57a462 214\r
215 // Special Interrupts (ID1020-ID1023) have an Interrupt ID greater than the number of interrupt (ie: Spurious interrupt).\r
2ca815a4 216 if ((GicInterrupt & ARM_GIC_ICCIAR_ACKINTID) >= mGicNumInterrupts) {\r
1e57a462 217 // The special interrupt do not need to be acknowledge\r
218 return;\r
219 }\r
220 \r
221 InterruptHandler = gRegisteredInterruptHandlers[GicInterrupt];\r
222 if (InterruptHandler != NULL) {\r
223 // Call the registered interrupt handler.\r
224 InterruptHandler (GicInterrupt, SystemContext);\r
225 } else {\r
226 DEBUG ((EFI_D_ERROR, "Spurious GIC interrupt: 0x%x\n", GicInterrupt));\r
227 }\r
228\r
229 EndOfInterrupt (&gHardwareInterruptProtocol, GicInterrupt);\r
230}\r
231\r
1e57a462 232//\r
233// The protocol instance produced by this driver\r
234//\r
235EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol = {\r
236 RegisterInterruptSource,\r
237 EnableInterruptSource,\r
238 DisableInterruptSource,\r
239 GetInterruptSourceState,\r
240 EndOfInterrupt\r
241};\r
242\r
243/**\r
244 Shutdown our hardware\r
245 \r
246 DXE Core will disable interrupts and turn off the timer and disable interrupts\r
247 after all the event handlers have run.\r
248\r
249 @param[in] Event The Event that is being processed\r
250 @param[in] Context Event Context\r
251**/\r
252VOID\r
253EFIAPI\r
254ExitBootServicesEvent (\r
255 IN EFI_EVENT Event,\r
256 IN VOID *Context\r
257 )\r
258{\r
259 UINTN Index;\r
260 \r
261 // Acknowledge all pending interrupts\r
262 for (Index = 0; Index < mGicNumInterrupts; Index++) {\r
263 DisableInterruptSource (&gHardwareInterruptProtocol, Index);\r
264 }\r
265\r
266 for (Index = 0; Index < mGicNumInterrupts; Index++) {\r
267 EndOfInterrupt (&gHardwareInterruptProtocol, Index);\r
268 }\r
269\r
270 // Disable Gic Interface\r
e700a1fc 271 ArmGicDisableInterruptInterface (PcdGet32(PcdGicInterruptInterfaceBase));\r
1e57a462 272\r
273 // Disable Gic Distributor\r
e700a1fc 274 ArmGicDisableDistributor (PcdGet32(PcdGicDistributorBase));\r
1e57a462 275}\r
276\r
277/**\r
278 Initialize the state information for the CPU Architectural Protocol\r
279\r
280 @param ImageHandle of the loaded driver\r
281 @param SystemTable Pointer to the System Table\r
282\r
283 @retval EFI_SUCCESS Protocol registered\r
284 @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure\r
285 @retval EFI_DEVICE_ERROR Hardware problems\r
286\r
287**/\r
288EFI_STATUS\r
289InterruptDxeInitialize (\r
290 IN EFI_HANDLE ImageHandle,\r
291 IN EFI_SYSTEM_TABLE *SystemTable\r
292 )\r
293{\r
294 EFI_STATUS Status;\r
295 UINTN Index;\r
296 UINT32 RegOffset;\r
297 UINTN RegShift;\r
1e57a462 298 UINT32 CpuTarget;\r
299 \r
1e57a462 300 // Make sure the Interrupt Controller Protocol is not already installed in the system.\r
301 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gHardwareInterruptProtocolGuid);\r
302\r
303 mGicNumInterrupts = ArmGicGetMaxNumInterrupts (PcdGet32(PcdGicDistributorBase));\r
304\r
305 for (Index = 0; Index < mGicNumInterrupts; Index++) {\r
306 DisableInterruptSource (&gHardwareInterruptProtocol, Index);\r
307 \r
308 // Set Priority \r
309 RegOffset = Index / 4;\r
310 RegShift = (Index % 4) * 8;\r
311 MmioAndThenOr32 (\r
312 PcdGet32(PcdGicDistributorBase) + ARM_GIC_ICDIPR + (4*RegOffset),\r
313 ~(0xff << RegShift), \r
314 ARM_GIC_DEFAULT_PRIORITY << RegShift\r
315 );\r
316 }\r
317\r
a1cca638
OM
318 //\r
319 // Targets the interrupts to the Primary Cpu\r
320 //\r
321\r
322 // Only Primary CPU will run this code. We can identify our GIC CPU ID by reading\r
323 // the GIC Distributor Target register. The 8 first GICD_ITARGETSRn are banked to each\r
324 // connected CPU. These 8 registers hold the CPU targets fields for interrupts 0-31.\r
325 // More Info in the GIC Specification about "Interrupt Processor Targets Registers"\r
326 //\r
327 // Read the first Interrupt Processor Targets Register (that corresponds to the 4\r
328 // first SGIs)\r
329 CpuTarget = MmioRead32 (PcdGet32 (PcdGicDistributorBase) + ARM_GIC_ICDIPTR);\r
330\r
331 // The CPU target is a bit field mapping each CPU to a GIC CPU Interface. This value\r
eb5c268f
OM
332 // is 0 when we run on a uniprocessor platform.\r
333 if (CpuTarget != 0) {\r
334 // The 8 first Interrupt Processor Targets Registers are read-only\r
335 for (Index = 8; Index < (mGicNumInterrupts / 4); Index++) {\r
336 MmioWrite32 (PcdGet32 (PcdGicDistributorBase) + ARM_GIC_ICDIPTR + (Index * 4), CpuTarget);\r
337 }\r
1e57a462 338 }\r
339\r
340 // Set binary point reg to 0x7 (no preemption)\r
341 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + ARM_GIC_ICCBPR, 0x7);\r
342\r
343 // Set priority mask reg to 0xff to allow all priorities through\r
344 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + ARM_GIC_ICCPMR, 0xff);\r
e700a1fc 345\r
1e57a462 346 // Enable gic cpu interface\r
e700a1fc 347 ArmGicEnableInterruptInterface (PcdGet32(PcdGicInterruptInterfaceBase));\r
1e57a462 348\r
349 // Enable gic distributor\r
e700a1fc
OM
350 ArmGicEnableDistributor (PcdGet32(PcdGicDistributorBase));\r
351\r
69b5dc9f
OM
352 Status = InstallAndRegisterInterruptService (\r
353 &gHardwareInterruptProtocol, IrqInterruptHandler, ExitBootServicesEvent);\r
1e57a462 354\r
355 return Status;\r
356}\r