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