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