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