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 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 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 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 99 ArmGicGetMaxNumInterrupts ( 100 IN INTN GicDistributorBase 101 ) 102 { 103 return 32 * ((MmioRead32 (GicDistributorBase + ARM_GIC_ICDICTR) & 0x1F) + 1); 104 } 105 106 VOID 107 EFIAPI 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 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 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 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 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 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 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 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 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