]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Drivers/PL390Gic/PL390GicDxe.c
ArmPkg/PL390GixDxe: Return from the interrupt handler when it is a sporadic interrupt
[mirror_edk2.git] / ArmPkg / Drivers / PL390Gic / PL390GicDxe.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 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 Module Name:
14
15 Gic.c
16
17 Abstract:
18
19 Driver implementing the GIC interrupt controller protocol
20
21 --*/
22
23 #include <PiDxe.h>
24
25 #include <Library/BaseLib.h>
26 #include <Library/DebugLib.h>
27 #include <Library/BaseMemoryLib.h>
28 #include <Library/UefiBootServicesTableLib.h>
29 #include <Library/UefiLib.h>
30 #include <Library/PcdLib.h>
31 #include <Library/IoLib.h>
32
33 #include <Protocol/Cpu.h>
34 #include <Protocol/HardwareInterrupt.h>
35
36 #include <Drivers/PL390Gic.h>
37
38 // number of 32-bit registers needed to represent those interrupts as a bit
39 // (used for enable set, enable clear, pending set, pending clear, and active regs)
40 #define GIC_NUM_REG_PER_INT_BITS (PcdGet32(PcdGicNumInterrupts) / 32)
41
42 // number of 32-bit registers needed to represent those interrupts as two bits
43 // (used for configuration reg)
44 #define GIC_NUM_REG_PER_INT_CFG (PcdGet32(PcdGicNumInterrupts) / 16)
45
46 // number of 32-bit registers needed to represent interrupts as 8-bit priority field
47 // (used for priority regs)
48 #define GIC_NUM_REG_PER_INT_BYTES (PcdGet32(PcdGicNumInterrupts) / 4)
49
50 #define GIC_DEFAULT_PRIORITY 0x80
51
52 extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol;
53
54 //
55 // Notifications
56 //
57 VOID *CpuProtocolNotificationToken = NULL;
58 EFI_EVENT CpuProtocolNotificationEvent = (EFI_EVENT)NULL;
59 EFI_EVENT EfiExitBootServicesEvent = (EFI_EVENT)NULL;
60
61 HARDWARE_INTERRUPT_HANDLER gRegisteredInterruptHandlers[FixedPcdGet32(PcdGicNumInterrupts)];
62
63 /**
64 Register Handler for the specified interrupt source.
65
66 @param This Instance pointer for this protocol
67 @param Source Hardware source of the interrupt
68 @param Handler Callback for interrupt. NULL to unregister
69
70 @retval EFI_SUCCESS Source was updated to support Handler.
71 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
72
73 **/
74 EFI_STATUS
75 EFIAPI
76 RegisterInterruptSource (
77 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
78 IN HARDWARE_INTERRUPT_SOURCE Source,
79 IN HARDWARE_INTERRUPT_HANDLER Handler
80 )
81 {
82 if (Source > PcdGet32(PcdGicNumInterrupts)) {
83 ASSERT(FALSE);
84 return EFI_UNSUPPORTED;
85 }
86
87 if ((Handler == NULL) && (gRegisteredInterruptHandlers[Source] == NULL)) {
88 return EFI_INVALID_PARAMETER;
89 }
90
91 if ((Handler != NULL) && (gRegisteredInterruptHandlers[Source] != NULL)) {
92 return EFI_ALREADY_STARTED;
93 }
94
95 gRegisteredInterruptHandlers[Source] = Handler;
96 return This->EnableInterruptSource(This, Source);
97 }
98
99 /**
100 Enable interrupt source Source.
101
102 @param This Instance pointer for this protocol
103 @param Source Hardware source of the interrupt
104
105 @retval EFI_SUCCESS Source interrupt enabled.
106 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
107
108 **/
109 EFI_STATUS
110 EFIAPI
111 EnableInterruptSource (
112 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
113 IN HARDWARE_INTERRUPT_SOURCE Source
114 )
115 {
116 UINT32 RegOffset;
117 UINTN RegShift;
118
119 if (Source > PcdGet32(PcdGicNumInterrupts)) {
120 ASSERT(FALSE);
121 return EFI_UNSUPPORTED;
122 }
123
124 // calculate enable register offset and bit position
125 RegOffset = Source / 32;
126 RegShift = Source % 32;
127
128 // write set-enable register
129 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + GIC_ICDISER+(4*RegOffset), 1 << RegShift);
130
131 return EFI_SUCCESS;
132 }
133
134 /**
135 Disable interrupt source Source.
136
137 @param This Instance pointer for this protocol
138 @param Source Hardware source of the interrupt
139
140 @retval EFI_SUCCESS Source interrupt disabled.
141 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
142
143 **/
144 EFI_STATUS
145 EFIAPI
146 DisableInterruptSource (
147 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
148 IN HARDWARE_INTERRUPT_SOURCE Source
149 )
150 {
151 UINT32 RegOffset;
152 UINTN RegShift;
153
154 if (Source > PcdGet32(PcdGicNumInterrupts)) {
155 ASSERT(FALSE);
156 return EFI_UNSUPPORTED;
157 }
158
159 // calculate enable register offset and bit position
160 RegOffset = Source / 32;
161 RegShift = Source % 32;
162
163 // write set-enable register
164 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + GIC_ICDICER+(4*RegOffset), 1 << RegShift);
165
166 return EFI_SUCCESS;
167 }
168
169 /**
170 Return current state of interrupt source Source.
171
172 @param This Instance pointer for this protocol
173 @param Source Hardware source of the interrupt
174 @param InterruptState TRUE: source enabled, FALSE: source disabled.
175
176 @retval EFI_SUCCESS InterruptState is valid
177 @retval EFI_DEVICE_ERROR InterruptState is not valid
178
179 **/
180 EFI_STATUS
181 EFIAPI
182 GetInterruptSourceState (
183 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
184 IN HARDWARE_INTERRUPT_SOURCE Source,
185 IN BOOLEAN *InterruptState
186 )
187 {
188 UINT32 RegOffset;
189 UINTN RegShift;
190
191 if (Source > PcdGet32(PcdGicNumInterrupts)) {
192 ASSERT(FALSE);
193 return EFI_UNSUPPORTED;
194 }
195
196 // calculate enable register offset and bit position
197 RegOffset = Source / 32;
198 RegShift = Source % 32;
199
200 if ((MmioRead32 (PcdGet32(PcdGicDistributorBase) + GIC_ICDISER+(4*RegOffset)) & (1<<RegShift)) == 0) {
201 *InterruptState = FALSE;
202 } else {
203 *InterruptState = TRUE;
204 }
205
206 return EFI_SUCCESS;
207 }
208
209 /**
210 Signal to the hardware that the End Of Intrrupt state
211 has been reached.
212
213 @param This Instance pointer for this protocol
214 @param Source Hardware source of the interrupt
215
216 @retval EFI_SUCCESS Source interrupt EOI'ed.
217 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
218
219 **/
220 EFI_STATUS
221 EFIAPI
222 EndOfInterrupt (
223 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
224 IN HARDWARE_INTERRUPT_SOURCE Source
225 )
226 {
227 if (Source > PcdGet32(PcdGicNumInterrupts)) {
228 ASSERT(FALSE);
229 return EFI_UNSUPPORTED;
230 }
231
232 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCEIOR, Source);
233 return EFI_SUCCESS;
234 }
235
236 /**
237 EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.
238
239 @param InterruptType Defines the type of interrupt or exception that
240 occurred on the processor.This parameter is processor architecture specific.
241 @param SystemContext A pointer to the processor context when
242 the interrupt occurred on the processor.
243
244 @return None
245
246 **/
247 VOID
248 EFIAPI
249 IrqInterruptHandler (
250 IN EFI_EXCEPTION_TYPE InterruptType,
251 IN EFI_SYSTEM_CONTEXT SystemContext
252 )
253 {
254 UINT32 GicInterrupt;
255 HARDWARE_INTERRUPT_HANDLER InterruptHandler;
256
257 GicInterrupt = MmioRead32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCIAR);
258 if (GicInterrupt >= PcdGet32(PcdGicNumInterrupts)) {
259 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCEIOR, GicInterrupt);
260 return;
261 }
262
263 InterruptHandler = gRegisteredInterruptHandlers[GicInterrupt];
264 if (InterruptHandler != NULL) {
265 // Call the registered interrupt handler.
266 InterruptHandler (GicInterrupt, SystemContext);
267 } else {
268 DEBUG ((EFI_D_ERROR, "Spurious GIC interrupt: 0x%x\n", GicInterrupt));
269 }
270
271 EndOfInterrupt (&gHardwareInterruptProtocol, GicInterrupt);
272 }
273
274 //
275 // Making this global saves a few bytes in image size
276 //
277 EFI_HANDLE gHardwareInterruptHandle = NULL;
278
279 //
280 // The protocol instance produced by this driver
281 //
282 EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol = {
283 RegisterInterruptSource,
284 EnableInterruptSource,
285 DisableInterruptSource,
286 GetInterruptSourceState,
287 EndOfInterrupt
288 };
289
290 /**
291 Shutdown our hardware
292
293 DXE Core will disable interrupts and turn off the timer and disable interrupts
294 after all the event handlers have run.
295
296 @param[in] Event The Event that is being processed
297 @param[in] Context Event Context
298 **/
299 VOID
300 EFIAPI
301 ExitBootServicesEvent (
302 IN EFI_EVENT Event,
303 IN VOID *Context
304 )
305 {
306 UINTN i;
307
308 for (i = 0; i < PcdGet32(PcdGicNumInterrupts); i++) {
309 DisableInterruptSource (&gHardwareInterruptProtocol, i);
310 }
311
312 // Acknowledge all pending interrupts
313 for (i = 0; i < PcdGet32(PcdGicNumInterrupts); i++) {
314 DisableInterruptSource (&gHardwareInterruptProtocol, i);
315 }
316
317 for (i = 0; i < PcdGet32(PcdGicNumInterrupts); i++) {
318 EndOfInterrupt (&gHardwareInterruptProtocol, i);
319 }
320
321 // Disable Gic Interface
322 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCICR, 0x0);
323 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCPMR, 0x0);
324
325 // Disable Gic Distributor
326 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + GIC_ICDDCR, 0x0);
327 }
328
329 //
330 // Notification routines
331 //
332 VOID
333 CpuProtocolInstalledNotification (
334 IN EFI_EVENT Event,
335 IN VOID *Context
336 )
337 {
338 EFI_STATUS Status;
339 EFI_CPU_ARCH_PROTOCOL *Cpu;
340
341 //
342 // Get the cpu protocol that this driver requires.
343 //
344 Status = gBS->LocateProtocol(&gEfiCpuArchProtocolGuid, NULL, (VOID **)&Cpu);
345 ASSERT_EFI_ERROR(Status);
346
347 //
348 // Unregister the default exception handler.
349 //
350 Status = Cpu->RegisterInterruptHandler(Cpu, EXCEPT_ARM_IRQ, NULL);
351 ASSERT_EFI_ERROR(Status);
352
353 //
354 // Register to receive interrupts
355 //
356 Status = Cpu->RegisterInterruptHandler(Cpu, EXCEPT_ARM_IRQ, IrqInterruptHandler);
357 ASSERT_EFI_ERROR(Status);
358 }
359
360 /**
361 Initialize the state information for the CPU Architectural Protocol
362
363 @param ImageHandle of the loaded driver
364 @param SystemTable Pointer to the System Table
365
366 @retval EFI_SUCCESS Protocol registered
367 @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
368 @retval EFI_DEVICE_ERROR Hardware problems
369
370 **/
371 EFI_STATUS
372 InterruptDxeInitialize (
373 IN EFI_HANDLE ImageHandle,
374 IN EFI_SYSTEM_TABLE *SystemTable
375 )
376 {
377 EFI_STATUS Status;
378 UINTN i;
379 UINT32 RegOffset;
380 UINTN RegShift;
381
382 // Make sure the Interrupt Controller Protocol is not already installed in the system.
383 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gHardwareInterruptProtocolGuid);
384
385 for (i = 0; i < PcdGet32(PcdGicNumInterrupts); i++) {
386 DisableInterruptSource (&gHardwareInterruptProtocol, i);
387
388 // Set Priority
389 RegOffset = i / 4;
390 RegShift = (i % 4) * 8;
391 MmioAndThenOr32 (
392 PcdGet32(PcdGicDistributorBase) + GIC_ICDIPR+(4*RegOffset),
393 ~(0xff << RegShift),
394 GIC_DEFAULT_PRIORITY << RegShift
395 );
396 }
397
398 // configure interrupts for cpu 0
399 for (i = 0; i < GIC_NUM_REG_PER_INT_BYTES; i++) {
400 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + GIC_ICDIPTR + (i*4), 0x01010101);
401 }
402
403 // set binary point reg to 0x7 (no preemption)
404 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCBPR, 0x7);
405
406 // set priority mask reg to 0xff to allow all priorities through
407 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCPMR, 0xff);
408
409 // enable gic cpu interface
410 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCICR, 0x1);
411
412 // enable gic distributor
413 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + GIC_ICDDCR, 0x1);
414
415 ZeroMem (&gRegisteredInterruptHandlers, sizeof (gRegisteredInterruptHandlers));
416
417 Status = gBS->InstallMultipleProtocolInterfaces (
418 &gHardwareInterruptHandle,
419 &gHardwareInterruptProtocolGuid, &gHardwareInterruptProtocol,
420 NULL
421 );
422 ASSERT_EFI_ERROR (Status);
423
424 // Set up to be notified when the Cpu protocol is installed.
425 Status = gBS->CreateEvent (EVT_NOTIFY_SIGNAL, TPL_CALLBACK, CpuProtocolInstalledNotification, NULL, &CpuProtocolNotificationEvent);
426 ASSERT_EFI_ERROR (Status);
427
428 Status = gBS->RegisterProtocolNotify (&gEfiCpuArchProtocolGuid, CpuProtocolNotificationEvent, (VOID *)&CpuProtocolNotificationToken);
429 ASSERT_EFI_ERROR (Status);
430
431 // Register for an ExitBootServicesEvent
432 Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_NOTIFY, ExitBootServicesEvent, NULL, &EfiExitBootServicesEvent);
433 ASSERT_EFI_ERROR (Status);
434
435 return Status;
436 }