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