Sync up ArmPkg with patch from mailing list. Changed name of BdsLib.h to BdsUnixLib...
[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);
260 }
261
262 InterruptHandler = gRegisteredInterruptHandlers[GicInterrupt];
263 if (InterruptHandler != NULL) {
264 // Call the registered interrupt handler.
265 InterruptHandler (GicInterrupt, SystemContext);
266 } else {
267 DEBUG ((EFI_D_ERROR, "Spurious GIC interrupt: 0x%x\n", GicInterrupt));
268 }
269
270 EndOfInterrupt (&gHardwareInterruptProtocol, GicInterrupt);
271}
272
273//
274// Making this global saves a few bytes in image size
275//
276EFI_HANDLE gHardwareInterruptHandle = NULL;
277
278//
279// The protocol instance produced by this driver
280//
281EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol = {
282 RegisterInterruptSource,
283 EnableInterruptSource,
284 DisableInterruptSource,
285 GetInterruptSourceState,
286 EndOfInterrupt
287};
288
289/**
290 Shutdown our hardware
291
292 DXE Core will disable interrupts and turn off the timer and disable interrupts
293 after all the event handlers have run.
294
295 @param[in] Event The Event that is being processed
296 @param[in] Context Event Context
297**/
298VOID
299EFIAPI
300ExitBootServicesEvent (
301 IN EFI_EVENT Event,
302 IN VOID *Context
303 )
304{
305 UINTN i;
306
307 for (i = 0; i < PcdGet32(PcdGicNumInterrupts); i++) {
308 DisableInterruptSource (&gHardwareInterruptProtocol, i);
309 }
310
311 // Acknowledge all pending interrupts
312 for (i = 0; i < PcdGet32(PcdGicNumInterrupts); i++) {
313 DisableInterruptSource (&gHardwareInterruptProtocol, i);
314 }
315
316 for (i = 0; i < PcdGet32(PcdGicNumInterrupts); i++) {
317 EndOfInterrupt (&gHardwareInterruptProtocol, i);
318 }
319
320 // Disable Gic Interface
321 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCICR, 0x0);
322 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCPMR, 0x0);
323
324 // Disable Gic Distributor
325 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + GIC_ICDDCR, 0x0);
326}
327
328//
329// Notification routines
330//
331VOID
332CpuProtocolInstalledNotification (
333 IN EFI_EVENT Event,
334 IN VOID *Context
335 )
336{
337 EFI_STATUS Status;
338 EFI_CPU_ARCH_PROTOCOL *Cpu;
339
340 //
341 // Get the cpu protocol that this driver requires.
342 //
343 Status = gBS->LocateProtocol(&gEfiCpuArchProtocolGuid, NULL, (VOID **)&Cpu);
344 ASSERT_EFI_ERROR(Status);
345
346 //
347 // Unregister the default exception handler.
348 //
349 Status = Cpu->RegisterInterruptHandler(Cpu, EXCEPT_ARM_IRQ, NULL);
350 ASSERT_EFI_ERROR(Status);
351
352 //
353 // Register to receive interrupts
354 //
355 Status = Cpu->RegisterInterruptHandler(Cpu, EXCEPT_ARM_IRQ, IrqInterruptHandler);
356 ASSERT_EFI_ERROR(Status);
357}
358
359/**
360 Initialize the state information for the CPU Architectural Protocol
361
362 @param ImageHandle of the loaded driver
363 @param SystemTable Pointer to the System Table
364
365 @retval EFI_SUCCESS Protocol registered
366 @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
367 @retval EFI_DEVICE_ERROR Hardware problems
368
369**/
370EFI_STATUS
371InterruptDxeInitialize (
372 IN EFI_HANDLE ImageHandle,
373 IN EFI_SYSTEM_TABLE *SystemTable
374 )
375{
376 EFI_STATUS Status;
377 UINTN i;
378 UINT32 RegOffset;
379 UINTN RegShift;
380
381 // Make sure the Interrupt Controller Protocol is not already installed in the system.
382 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gHardwareInterruptProtocolGuid);
383
384 for (i = 0; i < PcdGet32(PcdGicNumInterrupts); i++) {
385 DisableInterruptSource (&gHardwareInterruptProtocol, i);
386
387 // Set Priority
388 RegOffset = i / 4;
389 RegShift = (i % 4) * 8;
390 MmioAndThenOr32 (
391 PcdGet32(PcdGicDistributorBase) + GIC_ICDIPR+(4*RegOffset),
392 ~(0xff << RegShift),
393 GIC_DEFAULT_PRIORITY << RegShift
394 );
395 }
396
397 // configure interrupts for cpu 0
398 for (i = 0; i < GIC_NUM_REG_PER_INT_BYTES; i++) {
399 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + GIC_ICDIPTR + (i*4), 0x01010101);
400 }
401
402 // set binary point reg to 0x7 (no preemption)
403 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCBPR, 0x7);
404
405 // set priority mask reg to 0xff to allow all priorities through
406 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCPMR, 0xff);
407
408 // enable gic cpu interface
409 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCICR, 0x1);
410
411 // enable gic distributor
412 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + GIC_ICDDCR, 0x1);
413
414 ZeroMem (&gRegisteredInterruptHandlers, sizeof (gRegisteredInterruptHandlers));
415
416 Status = gBS->InstallMultipleProtocolInterfaces (
417 &gHardwareInterruptHandle,
418 &gHardwareInterruptProtocolGuid, &gHardwareInterruptProtocol,
419 NULL
420 );
421 ASSERT_EFI_ERROR (Status);
422
423 // Set up to be notified when the Cpu protocol is installed.
424 Status = gBS->CreateEvent (EVT_NOTIFY_SIGNAL, TPL_CALLBACK, CpuProtocolInstalledNotification, NULL, &CpuProtocolNotificationEvent);
425 ASSERT_EFI_ERROR (Status);
426
427 Status = gBS->RegisterProtocolNotify (&gEfiCpuArchProtocolGuid, CpuProtocolNotificationEvent, (VOID *)&CpuProtocolNotificationToken);
428 ASSERT_EFI_ERROR (Status);
429
430 // Register for an ExitBootServicesEvent
431 Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_NOTIFY, ExitBootServicesEvent, NULL, &EfiExitBootServicesEvent);
432 ASSERT_EFI_ERROR (Status);
433
434 return Status;
435}