]> git.proxmox.com Git - mirror_edk2.git/blame - 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
CommitLineData
1bfda055 1/*++
2
3Copyright (c) 2009, Hewlett-Packard Company. All rights reserved.<BR>
4Portions copyright (c) 2010, Apple Inc. All rights reserved.<BR>
5This program and the accompanying materials
6are licensed and made available under the terms and conditions of the BSD License
7which accompanies this distribution. The full text of the license may be found at
8http://opensource.org/licenses/bsd-license.php
9
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13Module Name:
14
15 Gic.c
16
17Abstract:
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
52extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol;
53
54//
55// Notifications
56//
57VOID *CpuProtocolNotificationToken = NULL;
58EFI_EVENT CpuProtocolNotificationEvent = (EFI_EVENT)NULL;
59EFI_EVENT EfiExitBootServicesEvent = (EFI_EVENT)NULL;
60
61HARDWARE_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**/
74EFI_STATUS
75EFIAPI
76RegisterInterruptSource (
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**/
109EFI_STATUS
110EFIAPI
111EnableInterruptSource (
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**/
144EFI_STATUS
145EFIAPI
146DisableInterruptSource (
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**/
180EFI_STATUS
181EFIAPI
182GetInterruptSourceState (
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**/
220EFI_STATUS
221EFIAPI
222EndOfInterrupt (
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**/
247VOID
248EFIAPI
249IrqInterruptHandler (
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);
fe93eba0 260 return;
1bfda055 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//
277EFI_HANDLE gHardwareInterruptHandle = NULL;
278
279//
280// The protocol instance produced by this driver
281//
282EFI_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**/
299VOID
300EFIAPI
301ExitBootServicesEvent (
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//
332VOID
333CpuProtocolInstalledNotification (
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**/
371EFI_STATUS
372InterruptDxeInitialize (
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}