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