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