]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPkg/Drivers/PL390Gic/PL390GicDxe.c
ArmPkg/CpuDxe: Change chain of dependency for CpuDxe and PL390Gic
[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//
1bfda055 57EFI_EVENT EfiExitBootServicesEvent = (EFI_EVENT)NULL;
58
59HARDWARE_INTERRUPT_HANDLER gRegisteredInterruptHandlers[FixedPcdGet32(PcdGicNumInterrupts)];
60
61/**
62 Register Handler for the specified interrupt source.
63
64 @param This Instance pointer for this protocol
65 @param Source Hardware source of the interrupt
66 @param Handler Callback for interrupt. NULL to unregister
67
68 @retval EFI_SUCCESS Source was updated to support Handler.
69 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
70
71**/
72EFI_STATUS
73EFIAPI
74RegisterInterruptSource (
75 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
76 IN HARDWARE_INTERRUPT_SOURCE Source,
77 IN HARDWARE_INTERRUPT_HANDLER Handler
78 )
79{
80 if (Source > PcdGet32(PcdGicNumInterrupts)) {
81 ASSERT(FALSE);
82 return EFI_UNSUPPORTED;
83 }
84
85 if ((Handler == NULL) && (gRegisteredInterruptHandlers[Source] == NULL)) {
86 return EFI_INVALID_PARAMETER;
87 }
88
89 if ((Handler != NULL) && (gRegisteredInterruptHandlers[Source] != NULL)) {
90 return EFI_ALREADY_STARTED;
91 }
92
93 gRegisteredInterruptHandlers[Source] = Handler;
94 return This->EnableInterruptSource(This, Source);
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**/
107EFI_STATUS
108EFIAPI
109EnableInterruptSource (
110 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
111 IN HARDWARE_INTERRUPT_SOURCE Source
112 )
113{
114 UINT32 RegOffset;
115 UINTN RegShift;
116
117 if (Source > PcdGet32(PcdGicNumInterrupts)) {
118 ASSERT(FALSE);
119 return EFI_UNSUPPORTED;
120 }
121
122 // calculate enable register offset and bit position
123 RegOffset = Source / 32;
124 RegShift = Source % 32;
125
126 // write set-enable register
9e2b420e 127 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + GIC_ICDISER + (4*RegOffset), 1 << RegShift);
1bfda055 128
129 return EFI_SUCCESS;
130}
131
132/**
133 Disable interrupt source Source.
134
135 @param This Instance pointer for this protocol
136 @param Source Hardware source of the interrupt
137
138 @retval EFI_SUCCESS Source interrupt disabled.
139 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
140
141**/
142EFI_STATUS
143EFIAPI
144DisableInterruptSource (
145 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
146 IN HARDWARE_INTERRUPT_SOURCE Source
147 )
148{
149 UINT32 RegOffset;
150 UINTN RegShift;
151
152 if (Source > PcdGet32(PcdGicNumInterrupts)) {
153 ASSERT(FALSE);
154 return EFI_UNSUPPORTED;
155 }
156
9e2b420e 157 // Calculate enable register offset and bit position
1bfda055 158 RegOffset = Source / 32;
159 RegShift = Source % 32;
160
9e2b420e 161 // Write set-enable register
162 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + GIC_ICDICER + (4*RegOffset), 1 << RegShift);
1bfda055 163
164 return EFI_SUCCESS;
165}
166
167/**
168 Return current state of interrupt source Source.
169
170 @param This Instance pointer for this protocol
171 @param Source Hardware source of the interrupt
172 @param InterruptState TRUE: source enabled, FALSE: source disabled.
173
174 @retval EFI_SUCCESS InterruptState is valid
175 @retval EFI_DEVICE_ERROR InterruptState is not valid
176
177**/
178EFI_STATUS
179EFIAPI
180GetInterruptSourceState (
181 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
182 IN HARDWARE_INTERRUPT_SOURCE Source,
183 IN BOOLEAN *InterruptState
184 )
185{
186 UINT32 RegOffset;
187 UINTN RegShift;
188
189 if (Source > PcdGet32(PcdGicNumInterrupts)) {
190 ASSERT(FALSE);
191 return EFI_UNSUPPORTED;
192 }
193
194 // calculate enable register offset and bit position
195 RegOffset = Source / 32;
196 RegShift = Source % 32;
197
9e2b420e 198 if ((MmioRead32 (PcdGet32(PcdGicDistributorBase) + GIC_ICDISER + (4*RegOffset)) & (1<<RegShift)) == 0) {
1bfda055 199 *InterruptState = FALSE;
200 } else {
201 *InterruptState = TRUE;
202 }
203
204 return EFI_SUCCESS;
205}
206
207/**
208 Signal to the hardware that the End Of Intrrupt state
209 has been reached.
210
211 @param This Instance pointer for this protocol
212 @param Source Hardware source of the interrupt
213
214 @retval EFI_SUCCESS Source interrupt EOI'ed.
215 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
216
217**/
218EFI_STATUS
219EFIAPI
220EndOfInterrupt (
221 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
222 IN HARDWARE_INTERRUPT_SOURCE Source
223 )
224{
225 if (Source > PcdGet32(PcdGicNumInterrupts)) {
226 ASSERT(FALSE);
227 return EFI_UNSUPPORTED;
228 }
229
230 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCEIOR, Source);
231 return EFI_SUCCESS;
232}
233
234/**
235 EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.
236
237 @param InterruptType Defines the type of interrupt or exception that
238 occurred on the processor.This parameter is processor architecture specific.
239 @param SystemContext A pointer to the processor context when
240 the interrupt occurred on the processor.
241
242 @return None
243
244**/
245VOID
246EFIAPI
247IrqInterruptHandler (
248 IN EFI_EXCEPTION_TYPE InterruptType,
249 IN EFI_SYSTEM_CONTEXT SystemContext
250 )
251{
252 UINT32 GicInterrupt;
253 HARDWARE_INTERRUPT_HANDLER InterruptHandler;
254
255 GicInterrupt = MmioRead32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCIAR);
256 if (GicInterrupt >= PcdGet32(PcdGicNumInterrupts)) {
257 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCEIOR, GicInterrupt);
fe93eba0 258 return;
1bfda055 259 }
260
261 InterruptHandler = gRegisteredInterruptHandlers[GicInterrupt];
262 if (InterruptHandler != NULL) {
263 // Call the registered interrupt handler.
264 InterruptHandler (GicInterrupt, SystemContext);
265 } else {
266 DEBUG ((EFI_D_ERROR, "Spurious GIC interrupt: 0x%x\n", GicInterrupt));
267 }
268
269 EndOfInterrupt (&gHardwareInterruptProtocol, GicInterrupt);
270}
271
272//
273// Making this global saves a few bytes in image size
274//
275EFI_HANDLE gHardwareInterruptHandle = NULL;
276
277//
278// The protocol instance produced by this driver
279//
280EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol = {
281 RegisterInterruptSource,
282 EnableInterruptSource,
283 DisableInterruptSource,
284 GetInterruptSourceState,
285 EndOfInterrupt
286};
287
288/**
289 Shutdown our hardware
290
291 DXE Core will disable interrupts and turn off the timer and disable interrupts
292 after all the event handlers have run.
293
294 @param[in] Event The Event that is being processed
295 @param[in] Context Event Context
296**/
297VOID
298EFIAPI
299ExitBootServicesEvent (
300 IN EFI_EVENT Event,
301 IN VOID *Context
302 )
303{
d7b6c49b 304 UINTN Index;
1bfda055 305
d7b6c49b 306 for (Index = 0; Index < PcdGet32(PcdGicNumInterrupts); Index++) {
307 DisableInterruptSource (&gHardwareInterruptProtocol, Index);
1bfda055 308 }
309
310 // Acknowledge all pending interrupts
d7b6c49b 311 for (Index = 0; Index < PcdGet32(PcdGicNumInterrupts); Index++) {
312 DisableInterruptSource (&gHardwareInterruptProtocol, Index);
1bfda055 313 }
314
d7b6c49b 315 for (Index = 0; Index < PcdGet32(PcdGicNumInterrupts); Index++) {
316 EndOfInterrupt (&gHardwareInterruptProtocol, Index);
1bfda055 317 }
318
319 // Disable Gic Interface
320 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCICR, 0x0);
321 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCPMR, 0x0);
322
323 // Disable Gic Distributor
324 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + GIC_ICDDCR, 0x0);
325}
326
1bfda055 327/**
328 Initialize the state information for the CPU Architectural Protocol
329
330 @param ImageHandle of the loaded driver
331 @param SystemTable Pointer to the System Table
332
333 @retval EFI_SUCCESS Protocol registered
334 @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
335 @retval EFI_DEVICE_ERROR Hardware problems
336
337**/
338EFI_STATUS
339InterruptDxeInitialize (
340 IN EFI_HANDLE ImageHandle,
341 IN EFI_SYSTEM_TABLE *SystemTable
342 )
343{
d7b6c49b 344 EFI_STATUS Status;
345 UINTN Index;
346 UINT32 RegOffset;
347 UINTN RegShift;
348 EFI_CPU_ARCH_PROTOCOL *Cpu;
1bfda055 349
350 // Make sure the Interrupt Controller Protocol is not already installed in the system.
351 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gHardwareInterruptProtocolGuid);
352
d7b6c49b 353 for (Index = 0; Index < PcdGet32(PcdGicNumInterrupts); Index++) {
354 DisableInterruptSource (&gHardwareInterruptProtocol, Index);
1bfda055 355
356 // Set Priority
d7b6c49b 357 RegOffset = Index / 4;
358 RegShift = (Index % 4) * 8;
1bfda055 359 MmioAndThenOr32 (
9e2b420e 360 PcdGet32(PcdGicDistributorBase) + GIC_ICDIPR + (4*RegOffset),
1bfda055 361 ~(0xff << RegShift),
362 GIC_DEFAULT_PRIORITY << RegShift
363 );
364 }
365
9e2b420e 366 // Configure interrupts for cpu 0
d7b6c49b 367 for (Index = 0; Index < GIC_NUM_REG_PER_INT_BYTES; Index++) {
368 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + GIC_ICDIPTR + (Index*4), 0x01010101);
1bfda055 369 }
370
9e2b420e 371 // Set binary point reg to 0x7 (no preemption)
1bfda055 372 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCBPR, 0x7);
373
9e2b420e 374 // Set priority mask reg to 0xff to allow all priorities through
1bfda055 375 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCPMR, 0xff);
376
9e2b420e 377 // Enable gic cpu interface
1bfda055 378 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCICR, 0x1);
379
9e2b420e 380 // Enable gic distributor
1bfda055 381 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + GIC_ICDDCR, 0x1);
382
383 ZeroMem (&gRegisteredInterruptHandlers, sizeof (gRegisteredInterruptHandlers));
384
385 Status = gBS->InstallMultipleProtocolInterfaces (
386 &gHardwareInterruptHandle,
387 &gHardwareInterruptProtocolGuid, &gHardwareInterruptProtocol,
388 NULL
389 );
390 ASSERT_EFI_ERROR (Status);
391
d7b6c49b 392 //
393 // Get the CPU protocol that this driver requires.
394 //
395 Status = gBS->LocateProtocol(&gEfiCpuArchProtocolGuid, NULL, (VOID **)&Cpu);
396 ASSERT_EFI_ERROR(Status);
1bfda055 397
d7b6c49b 398 //
399 // Unregister the default exception handler.
400 //
401 Status = Cpu->RegisterInterruptHandler(Cpu, EXCEPT_ARM_IRQ, NULL);
402 ASSERT_EFI_ERROR(Status);
403
404 //
405 // Register to receive interrupts
406 //
407 Status = Cpu->RegisterInterruptHandler(Cpu, EXCEPT_ARM_IRQ, IrqInterruptHandler);
408 ASSERT_EFI_ERROR(Status);
1bfda055 409
410 // Register for an ExitBootServicesEvent
411 Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_NOTIFY, ExitBootServicesEvent, NULL, &EfiExitBootServicesEvent);
412 ASSERT_EFI_ERROR (Status);
413
414 return Status;
415}