]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Drivers/PL390Gic/PL390GicDxe.c
ArmPkg/CpuDxe: Change chain of dependency for CpuDxe and PL390Gic
[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 EFI_EVENT EfiExitBootServicesEvent = (EFI_EVENT)NULL;
58
59 HARDWARE_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 **/
72 EFI_STATUS
73 EFIAPI
74 RegisterInterruptSource (
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 **/
107 EFI_STATUS
108 EFIAPI
109 EnableInterruptSource (
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
127 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + GIC_ICDISER + (4*RegOffset), 1 << RegShift);
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 **/
142 EFI_STATUS
143 EFIAPI
144 DisableInterruptSource (
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
157 // Calculate enable register offset and bit position
158 RegOffset = Source / 32;
159 RegShift = Source % 32;
160
161 // Write set-enable register
162 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + GIC_ICDICER + (4*RegOffset), 1 << RegShift);
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 **/
178 EFI_STATUS
179 EFIAPI
180 GetInterruptSourceState (
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
198 if ((MmioRead32 (PcdGet32(PcdGicDistributorBase) + GIC_ICDISER + (4*RegOffset)) & (1<<RegShift)) == 0) {
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 **/
218 EFI_STATUS
219 EFIAPI
220 EndOfInterrupt (
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 **/
245 VOID
246 EFIAPI
247 IrqInterruptHandler (
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);
258 return;
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 //
275 EFI_HANDLE gHardwareInterruptHandle = NULL;
276
277 //
278 // The protocol instance produced by this driver
279 //
280 EFI_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 **/
297 VOID
298 EFIAPI
299 ExitBootServicesEvent (
300 IN EFI_EVENT Event,
301 IN VOID *Context
302 )
303 {
304 UINTN Index;
305
306 for (Index = 0; Index < PcdGet32(PcdGicNumInterrupts); Index++) {
307 DisableInterruptSource (&gHardwareInterruptProtocol, Index);
308 }
309
310 // Acknowledge all pending interrupts
311 for (Index = 0; Index < PcdGet32(PcdGicNumInterrupts); Index++) {
312 DisableInterruptSource (&gHardwareInterruptProtocol, Index);
313 }
314
315 for (Index = 0; Index < PcdGet32(PcdGicNumInterrupts); Index++) {
316 EndOfInterrupt (&gHardwareInterruptProtocol, Index);
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
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 **/
338 EFI_STATUS
339 InterruptDxeInitialize (
340 IN EFI_HANDLE ImageHandle,
341 IN EFI_SYSTEM_TABLE *SystemTable
342 )
343 {
344 EFI_STATUS Status;
345 UINTN Index;
346 UINT32 RegOffset;
347 UINTN RegShift;
348 EFI_CPU_ARCH_PROTOCOL *Cpu;
349
350 // Make sure the Interrupt Controller Protocol is not already installed in the system.
351 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gHardwareInterruptProtocolGuid);
352
353 for (Index = 0; Index < PcdGet32(PcdGicNumInterrupts); Index++) {
354 DisableInterruptSource (&gHardwareInterruptProtocol, Index);
355
356 // Set Priority
357 RegOffset = Index / 4;
358 RegShift = (Index % 4) * 8;
359 MmioAndThenOr32 (
360 PcdGet32(PcdGicDistributorBase) + GIC_ICDIPR + (4*RegOffset),
361 ~(0xff << RegShift),
362 GIC_DEFAULT_PRIORITY << RegShift
363 );
364 }
365
366 // Configure interrupts for cpu 0
367 for (Index = 0; Index < GIC_NUM_REG_PER_INT_BYTES; Index++) {
368 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + GIC_ICDIPTR + (Index*4), 0x01010101);
369 }
370
371 // Set binary point reg to 0x7 (no preemption)
372 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCBPR, 0x7);
373
374 // Set priority mask reg to 0xff to allow all priorities through
375 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCPMR, 0xff);
376
377 // Enable gic cpu interface
378 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCICR, 0x1);
379
380 // Enable gic distributor
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
392 //
393 // Get the CPU protocol that this driver requires.
394 //
395 Status = gBS->LocateProtocol(&gEfiCpuArchProtocolGuid, NULL, (VOID **)&Cpu);
396 ASSERT_EFI_ERROR(Status);
397
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);
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 }