1/*++ 2 3Copyright (c) 2009, Hewlett-Packard Company. All rights reserved.<BR> 4Portions copyright (c) 2010, Apple Inc. All rights reserved.<BR> 5Portions copyright (c) 2011-2016, ARM Ltd. All rights reserved.<BR> 6 7This program and the accompanying materials 8are licensed and made available under the terms and conditions of the BSD License 9which accompanies this distribution. The full text of the license may be found at 10http://opensource.org/licenses/bsd-license.php 11 12THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 13WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 14 15Module Name: 16 17 GicV2/ArmGicV2Dxe.c 18 19Abstract: 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 31extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV2Protocol; 32 33STATIC UINT32 mGicInterruptInterfaceBase; 34STATIC 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**/ 46EFI_STATUS 47EFIAPI 48GicV2EnableInterruptSource ( 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**/ 73EFI_STATUS 74EFIAPI 75GicV2DisableInterruptSource ( 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**/ 101EFI_STATUS 102EFIAPI 103GicV2GetInterruptSourceState ( 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**/ 130EFI_STATUS 131EFIAPI 132GicV2EndOfInterrupt ( 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**/ 157VOID 158EFIAPI 159GicV2IrqInterruptHandler ( 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// 188EFI_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**/ 205VOID 206EFIAPI 207GicV2ExitBootServicesEvent ( 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**/ 247EFI_STATUS 248GicV2DxeInitialize ( 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