1 /** @file
2 *
3 * Copyright (c) 2011-2015, ARM Limited. All rights reserved.
4 *
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 **/
14
15 #include <Base.h>
16 #include <Library/ArmGicLib.h>
17 #include <Library/ArmLib.h>
18 #include <Library/DebugLib.h>
19 #include <Library/IoLib.h>
20 #include <Library/PcdLib.h>
21
22 /**
23 *
24 * Return whether the Source interrupt index refers to a shared interrupt (SPI)
25 */
26 STATIC
27 BOOLEAN
SourceIsSpi(IN UINTN Source)28 SourceIsSpi (
29 IN UINTN Source
30 )
31 {
32 return Source >= 32 && Source < 1020;
33 }
34
35 /**
36 * Return the base address of the GIC redistributor for the current CPU
37 *
38 * @param Revision GIC Revision. The GIC redistributor might have a different
39 * granularity following the GIC revision.
40 *
41 * @retval Base address of the associated GIC Redistributor
42 */
43 STATIC
44 UINTN
GicGetCpuRedistributorBase(IN UINTN GicRedistributorBase,IN ARM_GIC_ARCH_REVISION Revision)45 GicGetCpuRedistributorBase (
46 IN UINTN GicRedistributorBase,
47 IN ARM_GIC_ARCH_REVISION Revision
48 )
49 {
50 UINTN Index;
51 UINTN MpId;
52 UINTN CpuAffinity;
53 UINTN Affinity;
54 UINTN GicRedistributorGranularity;
55 UINTN GicCpuRedistributorBase;
56
57 MpId = ArmReadMpidr ();
58 // Define CPU affinity as Affinity0[0:8], Affinity1[9:15], Affinity2[16:23], Affinity3[24:32]
59 // whereas Affinity3 is defined at [32:39] in MPIDR
60 CpuAffinity = (MpId & (ARM_CORE_AFF0 | ARM_CORE_AFF1 | ARM_CORE_AFF2)) | ((MpId & ARM_CORE_AFF3) >> 8);
61
62 if (Revision == ARM_GIC_ARCH_REVISION_3) {
63 // 2 x 64KB frame: Redistributor control frame + SGI Control & Generation frame
64 GicRedistributorGranularity = ARM_GICR_CTLR_FRAME_SIZE + ARM_GICR_SGI_PPI_FRAME_SIZE;
65 } else {
66 ASSERT_EFI_ERROR (EFI_UNSUPPORTED);
67 return 0;
68 }
69
70 GicCpuRedistributorBase = GicRedistributorBase;
71
72 for (Index = 0; Index < PcdGet32 (PcdCoreCount); Index++) {
73 Affinity = MmioRead64 (GicCpuRedistributorBase + ARM_GICR_TYPER) >> 32;
74 if (Affinity == CpuAffinity) {
75 return GicCpuRedistributorBase;
76 }
77
78 // Move to the next GIC Redistributor frame
79 GicCpuRedistributorBase += GicRedistributorGranularity;
80 }
81
82 // The Redistributor has not been found for the current CPU
83 ASSERT_EFI_ERROR (EFI_NOT_FOUND);
84 return 0;
85 }
86
87 UINTN
88 EFIAPI
ArmGicGetInterfaceIdentification(IN INTN GicInterruptInterfaceBase)89 ArmGicGetInterfaceIdentification (
90 IN INTN GicInterruptInterfaceBase
91 )
92 {
93 // Read the GIC Identification Register
94 return MmioRead32 (GicInterruptInterfaceBase + ARM_GIC_ICCIIDR);
95 }
96
97 UINTN
98 EFIAPI
ArmGicGetMaxNumInterrupts(IN INTN GicDistributorBase)99 ArmGicGetMaxNumInterrupts (
100 IN INTN GicDistributorBase
101 )
102 {
103 return 32 * ((MmioRead32 (GicDistributorBase + ARM_GIC_ICDICTR) & 0x1F) + 1);
104 }
105
106 VOID
107 EFIAPI
ArmGicSendSgiTo(IN INTN GicDistributorBase,IN INTN TargetListFilter,IN INTN CPUTargetList,IN INTN SgiId)108 ArmGicSendSgiTo (
109 IN INTN GicDistributorBase,
110 IN INTN TargetListFilter,
111 IN INTN CPUTargetList,
112 IN INTN SgiId
113 )
114 {
115 MmioWrite32 (GicDistributorBase + ARM_GIC_ICDSGIR, ((TargetListFilter & 0x3) << 24) | ((CPUTargetList & 0xFF) << 16) | SgiId);
116 }
117
118 /*
119 * Acknowledge and return the value of the Interrupt Acknowledge Register
120 *
121 * InterruptId is returned separately from the register value because in
122 * the GICv2 the register value contains the CpuId and InterruptId while
123 * in the GICv3 the register value is only the InterruptId.
124 *
125 * @param GicInterruptInterfaceBase Base Address of the GIC CPU Interface
126 * @param InterruptId InterruptId read from the Interrupt Acknowledge Register
127 *
128 * @retval value returned by the Interrupt Acknowledge Register
129 *
130 */
131 UINTN
132 EFIAPI
ArmGicAcknowledgeInterrupt(IN UINTN GicInterruptInterfaceBase,OUT UINTN * InterruptId)133 ArmGicAcknowledgeInterrupt (
134 IN UINTN GicInterruptInterfaceBase,
135 OUT UINTN *InterruptId
136 )
137 {
138 UINTN Value;
139 ARM_GIC_ARCH_REVISION Revision;
140
141 Revision = ArmGicGetSupportedArchRevision ();
142 if (Revision == ARM_GIC_ARCH_REVISION_2) {
143 Value = ArmGicV2AcknowledgeInterrupt (GicInterruptInterfaceBase);
144 // InterruptId is required for the caller to know if a valid or spurious
145 // interrupt has been read
146 ASSERT (InterruptId != NULL);
147 if (InterruptId != NULL) {
148 *InterruptId = Value & ARM_GIC_ICCIAR_ACKINTID;
149 }
150 } else if (Revision == ARM_GIC_ARCH_REVISION_3) {
151 Value = ArmGicV3AcknowledgeInterrupt ();
152 } else {
153 ASSERT_EFI_ERROR (EFI_UNSUPPORTED);
154 // Report Spurious interrupt which is what the above controllers would
155 // return if no interrupt was available
156 Value = 1023;
157 }
158
159 return Value;
160 }
161
162 VOID
163 EFIAPI
ArmGicEndOfInterrupt(IN UINTN GicInterruptInterfaceBase,IN UINTN Source)164 ArmGicEndOfInterrupt (
165 IN UINTN GicInterruptInterfaceBase,
166 IN UINTN Source
167 )
168 {
169 ARM_GIC_ARCH_REVISION Revision;
170
171 Revision = ArmGicGetSupportedArchRevision ();
172 if (Revision == ARM_GIC_ARCH_REVISION_2) {
173 ArmGicV2EndOfInterrupt (GicInterruptInterfaceBase, Source);
174 } else if (Revision == ARM_GIC_ARCH_REVISION_3) {
175 ArmGicV3EndOfInterrupt (Source);
176 } else {
177 ASSERT_EFI_ERROR (EFI_UNSUPPORTED);
178 }
179 }
180
181 VOID
182 EFIAPI
ArmGicEnableInterrupt(IN UINTN GicDistributorBase,IN UINTN GicRedistributorBase,IN UINTN Source)183 ArmGicEnableInterrupt (
184 IN UINTN GicDistributorBase,
185 IN UINTN GicRedistributorBase,
186 IN UINTN Source
187 )
188 {
189 UINT32 RegOffset;
190 UINTN RegShift;
191 ARM_GIC_ARCH_REVISION Revision;
192 UINTN GicCpuRedistributorBase;
193
194 // Calculate enable register offset and bit position
195 RegOffset = Source / 32;
196 RegShift = Source % 32;
197
198 Revision = ArmGicGetSupportedArchRevision ();
199 if ((Revision == ARM_GIC_ARCH_REVISION_2) ||
200 FeaturePcdGet (PcdArmGicV3WithV2Legacy) ||
201 SourceIsSpi (Source)) {
202 // Write set-enable register
203 MmioWrite32 (GicDistributorBase + ARM_GIC_ICDISER + (4 * RegOffset), 1 << RegShift);
204 } else {
205 GicCpuRedistributorBase = GicGetCpuRedistributorBase (GicRedistributorBase, Revision);
206 if (GicCpuRedistributorBase == 0) {
207 ASSERT_EFI_ERROR (EFI_NOT_FOUND);
208 return;
209 }
210
211 // Write set-enable register
212 MmioWrite32 (GicCpuRedistributorBase + ARM_GICR_CTLR_FRAME_SIZE + ARM_GICR_ISENABLER + (4 * RegOffset), 1 << RegShift);
213 }
214 }
215
216 VOID
217 EFIAPI
ArmGicDisableInterrupt(IN UINTN GicDistributorBase,IN UINTN GicRedistributorBase,IN UINTN Source)218 ArmGicDisableInterrupt (
219 IN UINTN GicDistributorBase,
220 IN UINTN GicRedistributorBase,
221 IN UINTN Source
222 )
223 {
224 UINT32 RegOffset;
225 UINTN RegShift;
226 ARM_GIC_ARCH_REVISION Revision;
227 UINTN GicCpuRedistributorBase;
228
229 // Calculate enable register offset and bit position
230 RegOffset = Source / 32;
231 RegShift = Source % 32;
232
233 Revision = ArmGicGetSupportedArchRevision ();
234 if ((Revision == ARM_GIC_ARCH_REVISION_2) ||
235 FeaturePcdGet (PcdArmGicV3WithV2Legacy) ||
236 SourceIsSpi (Source)) {
237 // Write clear-enable register
238 MmioWrite32 (GicDistributorBase + ARM_GIC_ICDICER + (4 * RegOffset), 1 << RegShift);
239 } else {
240 GicCpuRedistributorBase = GicGetCpuRedistributorBase (GicRedistributorBase, Revision);
241 if (GicCpuRedistributorBase == 0) {
242 return;
243 }
244
245 // Write clear-enable register
246 MmioWrite32 (GicCpuRedistributorBase + ARM_GICR_CTLR_FRAME_SIZE + ARM_GICR_ICENABLER + (4 * RegOffset), 1 << RegShift);
247 }
248 }
249
250 BOOLEAN
251 EFIAPI
ArmGicIsInterruptEnabled(IN UINTN GicDistributorBase,IN UINTN GicRedistributorBase,IN UINTN Source)252 ArmGicIsInterruptEnabled (
253 IN UINTN GicDistributorBase,
254 IN UINTN GicRedistributorBase,
255 IN UINTN Source
256 )
257 {
258 UINT32 RegOffset;
259 UINTN RegShift;
260 ARM_GIC_ARCH_REVISION Revision;
261 UINTN GicCpuRedistributorBase;
262 UINT32 Interrupts;
263
264 // Calculate enable register offset and bit position
265 RegOffset = Source / 32;
266 RegShift = Source % 32;
267
268 Revision = ArmGicGetSupportedArchRevision ();
269 if ((Revision == ARM_GIC_ARCH_REVISION_2) ||
270 FeaturePcdGet (PcdArmGicV3WithV2Legacy) ||
271 SourceIsSpi (Source)) {
272 Interrupts = ((MmioRead32 (GicDistributorBase + ARM_GIC_ICDISER + (4 * RegOffset)) & (1 << RegShift)) != 0);
273 } else {
274 GicCpuRedistributorBase = GicGetCpuRedistributorBase (GicRedistributorBase, Revision);
275 if (GicCpuRedistributorBase == 0) {
276 return 0;
277 }
278
279 // Read set-enable register
280 Interrupts = MmioRead32 (GicCpuRedistributorBase + ARM_GICR_CTLR_FRAME_SIZE + ARM_GICR_ISENABLER + (4 * RegOffset));
281 }
282
283 return ((Interrupts & (1 << RegShift)) != 0);
284 }
285
286 VOID
287 EFIAPI
ArmGicDisableDistributor(IN INTN GicDistributorBase)288 ArmGicDisableDistributor (
289 IN INTN GicDistributorBase
290 )
291 {
292 // Disable Gic Distributor
293 MmioWrite32 (GicDistributorBase + ARM_GIC_ICDDCR, 0x0);
294 }
295
296 VOID
297 EFIAPI
ArmGicEnableInterruptInterface(IN INTN GicInterruptInterfaceBase)298 ArmGicEnableInterruptInterface (
299 IN INTN GicInterruptInterfaceBase
300 )
301 {
302 ARM_GIC_ARCH_REVISION Revision;
303
304 Revision = ArmGicGetSupportedArchRevision ();
305 if (Revision == ARM_GIC_ARCH_REVISION_2) {
306 ArmGicV2EnableInterruptInterface (GicInterruptInterfaceBase);
307 } else if (Revision == ARM_GIC_ARCH_REVISION_3) {
308 ArmGicV3EnableInterruptInterface ();
309 } else {
310 ASSERT_EFI_ERROR (EFI_UNSUPPORTED);
311 }
312 }
313
314 VOID
315 EFIAPI
ArmGicDisableInterruptInterface(IN INTN GicInterruptInterfaceBase)316 ArmGicDisableInterruptInterface (
317 IN INTN GicInterruptInterfaceBase
318 )
319 {
320 ARM_GIC_ARCH_REVISION Revision;
321
322 Revision = ArmGicGetSupportedArchRevision ();
323 if (Revision == ARM_GIC_ARCH_REVISION_2) {
324 ArmGicV2DisableInterruptInterface (GicInterruptInterfaceBase);
325 } else if (Revision == ARM_GIC_ARCH_REVISION_3) {
326 ArmGicV3DisableInterruptInterface ();
327 } else {
328 ASSERT_EFI_ERROR (EFI_UNSUPPORTED);
329 }
330 }
331