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-2016, 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 <Library/ArmGicLib.h>
26
27 #include "ArmGicDxe.h"
28
29 #define ARM_GIC_DEFAULT_PRIORITY 0x80
30
31 extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV2Protocol;
32
33 STATIC UINT32 mGicInterruptInterfaceBase;
34 STATIC UINT32 mGicDistributorBase;
35
36 /**
37 Enable interrupt source Source.
38
39 @param This Instance pointer for this protocol
40 @param Source Hardware source of the interrupt
41
42 @retval EFI_SUCCESS Source interrupt enabled.
43 @retval EFI_UNSUPPORTED Source interrupt is not supported
44
45 **/
46 EFI_STATUS
47 EFIAPI
GicV2EnableInterruptSource(IN EFI_HARDWARE_INTERRUPT_PROTOCOL * This,IN HARDWARE_INTERRUPT_SOURCE Source)48 GicV2EnableInterruptSource (
49 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
50 IN HARDWARE_INTERRUPT_SOURCE Source
51 )
52 {
53 if (Source >= mGicNumInterrupts) {
54 ASSERT(FALSE);
55 return EFI_UNSUPPORTED;
56 }
57
58 ArmGicEnableInterrupt (mGicDistributorBase, 0, Source);
59
60 return EFI_SUCCESS;
61 }
62
63 /**
64 Disable interrupt source Source.
65
66 @param This Instance pointer for this protocol
67 @param Source Hardware source of the interrupt
68
69 @retval EFI_SUCCESS Source interrupt disabled.
70 @retval EFI_UNSUPPORTED Source interrupt is not supported
71
72 **/
73 EFI_STATUS
74 EFIAPI
GicV2DisableInterruptSource(IN EFI_HARDWARE_INTERRUPT_PROTOCOL * This,IN HARDWARE_INTERRUPT_SOURCE Source)75 GicV2DisableInterruptSource (
76 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
77 IN HARDWARE_INTERRUPT_SOURCE Source
78 )
79 {
80 if (Source >= mGicNumInterrupts) {
81 ASSERT(FALSE);
82 return EFI_UNSUPPORTED;
83 }
84
85 ArmGicDisableInterrupt (mGicDistributorBase, 0, Source);
86
87 return EFI_SUCCESS;
88 }
89
90 /**
91 Return current state of interrupt source Source.
92
93 @param This Instance pointer for this protocol
94 @param Source Hardware source of the interrupt
95 @param InterruptState TRUE: source enabled, FALSE: source disabled.
96
97 @retval EFI_SUCCESS InterruptState is valid
98 @retval EFI_UNSUPPORTED Source interrupt is not supported
99
100 **/
101 EFI_STATUS
102 EFIAPI
GicV2GetInterruptSourceState(IN EFI_HARDWARE_INTERRUPT_PROTOCOL * This,IN HARDWARE_INTERRUPT_SOURCE Source,IN BOOLEAN * InterruptState)103 GicV2GetInterruptSourceState (
104 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
105 IN HARDWARE_INTERRUPT_SOURCE Source,
106 IN BOOLEAN *InterruptState
107 )
108 {
109 if (Source >= mGicNumInterrupts) {
110 ASSERT(FALSE);
111 return EFI_UNSUPPORTED;
112 }
113
114 *InterruptState = ArmGicIsInterruptEnabled (mGicDistributorBase, 0, Source);
115
116 return EFI_SUCCESS;
117 }
118
119 /**
120 Signal to the hardware that the End Of Interrupt state
121 has been reached.
122
123 @param This Instance pointer for this protocol
124 @param Source Hardware source of the interrupt
125
126 @retval EFI_SUCCESS Source interrupt EOI'ed.
127 @retval EFI_UNSUPPORTED Source interrupt is not supported
128
129 **/
130 EFI_STATUS
131 EFIAPI
GicV2EndOfInterrupt(IN EFI_HARDWARE_INTERRUPT_PROTOCOL * This,IN HARDWARE_INTERRUPT_SOURCE Source)132 GicV2EndOfInterrupt (
133 IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
134 IN HARDWARE_INTERRUPT_SOURCE Source
135 )
136 {
137 if (Source >= mGicNumInterrupts) {
138 ASSERT(FALSE);
139 return EFI_UNSUPPORTED;
140 }
141
142 ArmGicV2EndOfInterrupt (mGicInterruptInterfaceBase, Source);
143 return EFI_SUCCESS;
144 }
145
146 /**
147 EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.
148
149 @param InterruptType Defines the type of interrupt or exception that
150 occurred on the processor.This parameter is processor architecture specific.
151 @param SystemContext A pointer to the processor context when
152 the interrupt occurred on the processor.
153
154 @return None
155
156 **/
157 VOID
158 EFIAPI
GicV2IrqInterruptHandler(IN EFI_EXCEPTION_TYPE InterruptType,IN EFI_SYSTEM_CONTEXT SystemContext)159 GicV2IrqInterruptHandler (
160 IN EFI_EXCEPTION_TYPE InterruptType,
161 IN EFI_SYSTEM_CONTEXT SystemContext
162 )
163 {
164 UINT32 GicInterrupt;
165 HARDWARE_INTERRUPT_HANDLER InterruptHandler;
166
167 GicInterrupt = ArmGicV2AcknowledgeInterrupt (mGicInterruptInterfaceBase);
168
169 // Special Interrupts (ID1020-ID1023) have an Interrupt ID greater than the number of interrupt (ie: Spurious interrupt).
170 if ((GicInterrupt & ARM_GIC_ICCIAR_ACKINTID) >= mGicNumInterrupts) {
171 // The special interrupt do not need to be acknowledge
172 return;
173 }
174
175 InterruptHandler = gRegisteredInterruptHandlers[GicInterrupt];
176 if (InterruptHandler != NULL) {
177 // Call the registered interrupt handler.
178 InterruptHandler (GicInterrupt, SystemContext);
179 } else {
180 DEBUG ((EFI_D_ERROR, "Spurious GIC interrupt: 0x%x\n", GicInterrupt));
181 GicV2EndOfInterrupt (&gHardwareInterruptV2Protocol, GicInterrupt);
182 }
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
GicV2ExitBootServicesEvent(IN EFI_EVENT Event,IN VOID * Context)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
GicV2DxeInitialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)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 = PcdGet64 (PcdGicInterruptInterfaceBase);
263 mGicDistributorBase = PcdGet64 (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 }
318