]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Drivers/ArmGic/GicV2/ArmGicV2Dxe.c
ArmPkg: allow dynamic GIC base addresses
[mirror_edk2.git] / ArmPkg / Drivers / ArmGic / GicV2 / ArmGicV2Dxe.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-2014, ARM Ltd. All rights reserved.<BR>
6
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
11
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14
15 Module Name:
16
17 GicV2/ArmGicV2Dxe.c
18
19 Abstract:
20
21 Driver implementing the GicV2 interrupt controller protocol
22
23 --*/
24
25 #include "ArmGicDxe.h"
26 #include "GicV2/ArmGicV2Lib.h"
27
28 #define ARM_GIC_DEFAULT_PRIORITY 0x80
29
30 extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV2Protocol;
31
32 UINT32 mGicInterruptInterfaceBase;
33 UINT32 mGicDistributorBase;
34
35 /**
36 Enable interrupt source Source.
37
38 @param This Instance pointer for this protocol
39 @param Source Hardware source of the interrupt
40
41 @retval EFI_SUCCESS Source interrupt enabled.
42 @retval EFI_UNSUPPORTED Source interrupt is not supported
43
44 **/
45 EFI_STATUS
46 EFIAPI
47 GicV2EnableInterruptSource (
48 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
49 IN HARDWARE_INTERRUPT_SOURCE Source
50 )
51 {
52 if (Source > mGicNumInterrupts) {
53 ASSERT(FALSE);
54 return EFI_UNSUPPORTED;
55 }
56
57 ArmGicEnableInterrupt (mGicDistributorBase, Source);
58
59 return EFI_SUCCESS;
60 }
61
62 /**
63 Disable interrupt source Source.
64
65 @param This Instance pointer for this protocol
66 @param Source Hardware source of the interrupt
67
68 @retval EFI_SUCCESS Source interrupt disabled.
69 @retval EFI_UNSUPPORTED Source interrupt is not supported
70
71 **/
72 EFI_STATUS
73 EFIAPI
74 GicV2DisableInterruptSource (
75 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
76 IN HARDWARE_INTERRUPT_SOURCE Source
77 )
78 {
79 if (Source > mGicNumInterrupts) {
80 ASSERT(FALSE);
81 return EFI_UNSUPPORTED;
82 }
83
84 ArmGicDisableInterrupt (mGicDistributorBase, Source);
85
86 return EFI_SUCCESS;
87 }
88
89 /**
90 Return current state of interrupt source Source.
91
92 @param This Instance pointer for this protocol
93 @param Source Hardware source of the interrupt
94 @param InterruptState TRUE: source enabled, FALSE: source disabled.
95
96 @retval EFI_SUCCESS InterruptState is valid
97 @retval EFI_UNSUPPORTED Source interrupt is not supported
98
99 **/
100 EFI_STATUS
101 EFIAPI
102 GicV2GetInterruptSourceState (
103 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
104 IN HARDWARE_INTERRUPT_SOURCE Source,
105 IN BOOLEAN *InterruptState
106 )
107 {
108 if (Source > mGicNumInterrupts) {
109 ASSERT(FALSE);
110 return EFI_UNSUPPORTED;
111 }
112
113 *InterruptState = ArmGicIsInterruptEnabled (mGicDistributorBase, Source);
114
115 return EFI_SUCCESS;
116 }
117
118 /**
119 Signal to the hardware that the End Of Interrupt state
120 has been reached.
121
122 @param This Instance pointer for this protocol
123 @param Source Hardware source of the interrupt
124
125 @retval EFI_SUCCESS Source interrupt EOI'ed.
126 @retval EFI_UNSUPPORTED Source interrupt is not supported
127
128 **/
129 EFI_STATUS
130 EFIAPI
131 GicV2EndOfInterrupt (
132 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
133 IN HARDWARE_INTERRUPT_SOURCE Source
134 )
135 {
136 if (Source > mGicNumInterrupts) {
137 ASSERT(FALSE);
138 return EFI_UNSUPPORTED;
139 }
140
141 ArmGicV2EndOfInterrupt (mGicInterruptInterfaceBase, Source);
142 return EFI_SUCCESS;
143 }
144
145 /**
146 EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.
147
148 @param InterruptType Defines the type of interrupt or exception that
149 occurred on the processor.This parameter is processor architecture specific.
150 @param SystemContext A pointer to the processor context when
151 the interrupt occurred on the processor.
152
153 @return None
154
155 **/
156 VOID
157 EFIAPI
158 GicV2IrqInterruptHandler (
159 IN EFI_EXCEPTION_TYPE InterruptType,
160 IN EFI_SYSTEM_CONTEXT SystemContext
161 )
162 {
163 UINT32 GicInterrupt;
164 HARDWARE_INTERRUPT_HANDLER InterruptHandler;
165
166 GicInterrupt = ArmGicV2AcknowledgeInterrupt (mGicInterruptInterfaceBase);
167
168 // Special Interrupts (ID1020-ID1023) have an Interrupt ID greater than the number of interrupt (ie: Spurious interrupt).
169 if ((GicInterrupt & ARM_GIC_ICCIAR_ACKINTID) >= mGicNumInterrupts) {
170 // The special interrupt do not need to be acknowledge
171 return;
172 }
173
174 InterruptHandler = gRegisteredInterruptHandlers[GicInterrupt];
175 if (InterruptHandler != NULL) {
176 // Call the registered interrupt handler.
177 InterruptHandler (GicInterrupt, SystemContext);
178 } else {
179 DEBUG ((EFI_D_ERROR, "Spurious GIC interrupt: 0x%x\n", GicInterrupt));
180 }
181
182 GicV2EndOfInterrupt (&gHardwareInterruptV2Protocol, GicInterrupt);
183 }
184
185 //
186 // The protocol instance produced by this driver
187 //
188 EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV2Protocol = {
189 RegisterInterruptSource,
190 GicV2EnableInterruptSource,
191 GicV2DisableInterruptSource,
192 GicV2GetInterruptSourceState,
193 GicV2EndOfInterrupt
194 };
195
196 /**
197 Shutdown our hardware
198
199 DXE Core will disable interrupts and turn off the timer and disable interrupts
200 after all the event handlers have run.
201
202 @param[in] Event The Event that is being processed
203 @param[in] Context Event Context
204 **/
205 VOID
206 EFIAPI
207 GicV2ExitBootServicesEvent (
208 IN EFI_EVENT Event,
209 IN VOID *Context
210 )
211 {
212 UINTN Index;
213 UINT32 GicInterrupt;
214
215 // Disable all the interrupts
216 for (Index = 0; Index < mGicNumInterrupts; Index++) {
217 GicV2DisableInterruptSource (&gHardwareInterruptV2Protocol, Index);
218 }
219
220 // Acknowledge all pending interrupts
221 do {
222 GicInterrupt = ArmGicV2AcknowledgeInterrupt (mGicInterruptInterfaceBase);
223
224 if ((GicInterrupt & ARM_GIC_ICCIAR_ACKINTID) < mGicNumInterrupts) {
225 GicV2EndOfInterrupt (&gHardwareInterruptV2Protocol, GicInterrupt);
226 }
227 } while (!ARM_GIC_IS_SPECIAL_INTERRUPTS (GicInterrupt));
228
229 // Disable Gic Interface
230 ArmGicV2DisableInterruptInterface (mGicInterruptInterfaceBase);
231
232 // Disable Gic Distributor
233 ArmGicDisableDistributor (mGicDistributorBase);
234 }
235
236 /**
237 Initialize the state information for the CPU Architectural Protocol
238
239 @param ImageHandle of the loaded driver
240 @param SystemTable Pointer to the System Table
241
242 @retval EFI_SUCCESS Protocol registered
243 @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
244 @retval EFI_DEVICE_ERROR Hardware problems
245
246 **/
247 EFI_STATUS
248 GicV2DxeInitialize (
249 IN EFI_HANDLE ImageHandle,
250 IN EFI_SYSTEM_TABLE *SystemTable
251 )
252 {
253 EFI_STATUS Status;
254 UINTN Index;
255 UINT32 RegOffset;
256 UINTN RegShift;
257 UINT32 CpuTarget;
258
259 // Make sure the Interrupt Controller Protocol is not already installed in the system.
260 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gHardwareInterruptProtocolGuid);
261
262 mGicInterruptInterfaceBase = PcdGet32 (PcdGicInterruptInterfaceBase);
263 mGicDistributorBase = PcdGet32 (PcdGicDistributorBase);
264 mGicNumInterrupts = ArmGicGetMaxNumInterrupts (mGicDistributorBase);
265
266 for (Index = 0; Index < mGicNumInterrupts; Index++) {
267 GicV2DisableInterruptSource (&gHardwareInterruptV2Protocol, Index);
268
269 // Set Priority
270 RegOffset = Index / 4;
271 RegShift = (Index % 4) * 8;
272 MmioAndThenOr32 (
273 mGicDistributorBase + ARM_GIC_ICDIPR + (4 * RegOffset),
274 ~(0xff << RegShift),
275 ARM_GIC_DEFAULT_PRIORITY << RegShift
276 );
277 }
278
279 //
280 // Targets the interrupts to the Primary Cpu
281 //
282
283 // Only Primary CPU will run this code. We can identify our GIC CPU ID by reading
284 // the GIC Distributor Target register. The 8 first GICD_ITARGETSRn are banked to each
285 // connected CPU. These 8 registers hold the CPU targets fields for interrupts 0-31.
286 // More Info in the GIC Specification about "Interrupt Processor Targets Registers"
287 //
288 // Read the first Interrupt Processor Targets Register (that corresponds to the 4
289 // first SGIs)
290 CpuTarget = MmioRead32 (mGicDistributorBase + ARM_GIC_ICDIPTR);
291
292 // The CPU target is a bit field mapping each CPU to a GIC CPU Interface. This value
293 // is 0 when we run on a uniprocessor platform.
294 if (CpuTarget != 0) {
295 // The 8 first Interrupt Processor Targets Registers are read-only
296 for (Index = 8; Index < (mGicNumInterrupts / 4); Index++) {
297 MmioWrite32 (mGicDistributorBase + ARM_GIC_ICDIPTR + (Index * 4), CpuTarget);
298 }
299 }
300
301 // Set binary point reg to 0x7 (no preemption)
302 MmioWrite32 (mGicInterruptInterfaceBase + ARM_GIC_ICCBPR, 0x7);
303
304 // Set priority mask reg to 0xff to allow all priorities through
305 MmioWrite32 (mGicInterruptInterfaceBase + ARM_GIC_ICCPMR, 0xff);
306
307 // Enable gic cpu interface
308 ArmGicEnableInterruptInterface (mGicInterruptInterfaceBase);
309
310 // Enable gic distributor
311 ArmGicEnableDistributor (mGicDistributorBase);
312
313 Status = InstallAndRegisterInterruptService (
314 &gHardwareInterruptV2Protocol, GicV2IrqInterruptHandler, GicV2ExitBootServicesEvent);
315
316 return Status;
317 }