]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Drivers/PL390Gic/PL390GicDxe.c
02853a2f31878e093056879a7137202a93f8f75f
[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
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) + 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) + 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) + 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) + 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) + GIC_ICCIAR);
262 if (GicInterrupt >= PcdGet32(PcdGicNumInterrupts)) {
263 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + 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) + GIC_ICCICR, 0x0);
327 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCPMR, 0x0);
328
329 // Disable Gic Distributor
330 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + 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) + GIC_ICDIPR + (4*RegOffset),
367 ~(0xff << RegShift),
368 GIC_DEFAULT_PRIORITY << RegShift
369 );
370 }
371
372 // Configure interrupts for cpu 0
373 for (Index = 0; Index < GIC_NUM_REG_PER_INT_BYTES; Index++) {
374 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + GIC_ICDIPTR + (Index*4), 0x01010101);
375 }
376
377 // Set binary point reg to 0x7 (no preemption)
378 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCBPR, 0x7);
379
380 // Set priority mask reg to 0xff to allow all priorities through
381 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCPMR, 0xff);
382
383 // Enable gic cpu interface
384 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase) + GIC_ICCICR, 0x1);
385
386 // Enable gic distributor
387 MmioWrite32 (PcdGet32(PcdGicDistributorBase) + 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 }