• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <Library/ArmGicLib.h>
16 
17 #include "ArmGicDxe.h"
18 
19 #define ARM_GIC_DEFAULT_PRIORITY  0x80
20 
21 extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV3Protocol;
22 
23 STATIC UINTN mGicDistributorBase;
24 STATIC UINTN mGicRedistributorsBase;
25 
26 /**
27   Enable interrupt source Source.
28 
29   @param This     Instance pointer for this protocol
30   @param Source   Hardware source of the interrupt
31 
32   @retval EFI_SUCCESS       Source interrupt enabled.
33   @retval EFI_DEVICE_ERROR  Hardware could not be programmed.
34 
35 **/
36 EFI_STATUS
37 EFIAPI
GicV3EnableInterruptSource(IN EFI_HARDWARE_INTERRUPT_PROTOCOL * This,IN HARDWARE_INTERRUPT_SOURCE Source)38 GicV3EnableInterruptSource (
39   IN EFI_HARDWARE_INTERRUPT_PROTOCOL    *This,
40   IN HARDWARE_INTERRUPT_SOURCE          Source
41   )
42 {
43   if (Source > mGicNumInterrupts) {
44     ASSERT(FALSE);
45     return EFI_UNSUPPORTED;
46   }
47 
48   ArmGicEnableInterrupt (mGicDistributorBase, mGicRedistributorsBase, Source);
49 
50   return EFI_SUCCESS;
51 }
52 
53 /**
54   Disable interrupt source Source.
55 
56   @param This     Instance pointer for this protocol
57   @param Source   Hardware source of the interrupt
58 
59   @retval EFI_SUCCESS       Source interrupt disabled.
60   @retval EFI_DEVICE_ERROR  Hardware could not be programmed.
61 
62 **/
63 EFI_STATUS
64 EFIAPI
GicV3DisableInterruptSource(IN EFI_HARDWARE_INTERRUPT_PROTOCOL * This,IN HARDWARE_INTERRUPT_SOURCE Source)65 GicV3DisableInterruptSource (
66   IN EFI_HARDWARE_INTERRUPT_PROTOCOL    *This,
67   IN HARDWARE_INTERRUPT_SOURCE          Source
68   )
69 {
70   if (Source > mGicNumInterrupts) {
71     ASSERT(FALSE);
72     return EFI_UNSUPPORTED;
73   }
74 
75   ArmGicDisableInterrupt (mGicDistributorBase, mGicRedistributorsBase, Source);
76 
77   return EFI_SUCCESS;
78 }
79 
80 /**
81   Return current state of interrupt source Source.
82 
83   @param This     Instance pointer for this protocol
84   @param Source   Hardware source of the interrupt
85   @param InterruptState  TRUE: source enabled, FALSE: source disabled.
86 
87   @retval EFI_SUCCESS       InterruptState is valid
88   @retval EFI_DEVICE_ERROR  InterruptState is not valid
89 
90 **/
91 EFI_STATUS
92 EFIAPI
GicV3GetInterruptSourceState(IN EFI_HARDWARE_INTERRUPT_PROTOCOL * This,IN HARDWARE_INTERRUPT_SOURCE Source,IN BOOLEAN * InterruptState)93 GicV3GetInterruptSourceState (
94   IN EFI_HARDWARE_INTERRUPT_PROTOCOL    *This,
95   IN HARDWARE_INTERRUPT_SOURCE          Source,
96   IN BOOLEAN                            *InterruptState
97   )
98 {
99   if (Source > mGicNumInterrupts) {
100     ASSERT(FALSE);
101     return EFI_UNSUPPORTED;
102   }
103 
104   *InterruptState = ArmGicIsInterruptEnabled (mGicDistributorBase, mGicRedistributorsBase, Source);
105 
106   return EFI_SUCCESS;
107 }
108 
109 /**
110   Signal to the hardware that the End Of Interrupt state
111   has been reached.
112 
113   @param This     Instance pointer for this protocol
114   @param Source   Hardware source of the interrupt
115 
116   @retval EFI_SUCCESS       Source interrupt EOI'ed.
117   @retval EFI_DEVICE_ERROR  Hardware could not be programmed.
118 
119 **/
120 EFI_STATUS
121 EFIAPI
GicV3EndOfInterrupt(IN EFI_HARDWARE_INTERRUPT_PROTOCOL * This,IN HARDWARE_INTERRUPT_SOURCE Source)122 GicV3EndOfInterrupt (
123   IN EFI_HARDWARE_INTERRUPT_PROTOCOL    *This,
124   IN HARDWARE_INTERRUPT_SOURCE          Source
125   )
126 {
127   if (Source > mGicNumInterrupts) {
128     ASSERT(FALSE);
129     return EFI_UNSUPPORTED;
130   }
131 
132   ArmGicV3EndOfInterrupt (Source);
133   return EFI_SUCCESS;
134 }
135 
136 /**
137   EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.
138 
139   @param  InterruptType    Defines the type of interrupt or exception that
140                            occurred on the processor.This parameter is processor architecture specific.
141   @param  SystemContext    A pointer to the processor context when
142                            the interrupt occurred on the processor.
143 
144   @return None
145 
146 **/
147 VOID
148 EFIAPI
GicV3IrqInterruptHandler(IN EFI_EXCEPTION_TYPE InterruptType,IN EFI_SYSTEM_CONTEXT SystemContext)149 GicV3IrqInterruptHandler (
150   IN EFI_EXCEPTION_TYPE           InterruptType,
151   IN EFI_SYSTEM_CONTEXT           SystemContext
152   )
153 {
154   UINT32                      GicInterrupt;
155   HARDWARE_INTERRUPT_HANDLER  InterruptHandler;
156 
157   GicInterrupt = ArmGicV3AcknowledgeInterrupt ();
158 
159   // Special Interrupts (ID1020-ID1023) have an Interrupt ID greater than the
160   // number of interrupt (ie: Spurious interrupt).
161   if ((GicInterrupt & ARM_GIC_ICCIAR_ACKINTID) >= mGicNumInterrupts) {
162     // The special interrupt do not need to be acknowledge
163     return;
164   }
165 
166   InterruptHandler = gRegisteredInterruptHandlers[GicInterrupt];
167   if (InterruptHandler != NULL) {
168     // Call the registered interrupt handler.
169     InterruptHandler (GicInterrupt, SystemContext);
170   } else {
171     DEBUG ((EFI_D_ERROR, "Spurious GIC interrupt: 0x%x\n", GicInterrupt));
172   }
173 
174   GicV3EndOfInterrupt (&gHardwareInterruptV3Protocol, GicInterrupt);
175 }
176 
177 //
178 // The protocol instance produced by this driver
179 //
180 EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV3Protocol = {
181   RegisterInterruptSource,
182   GicV3EnableInterruptSource,
183   GicV3DisableInterruptSource,
184   GicV3GetInterruptSourceState,
185   GicV3EndOfInterrupt
186 };
187 
188 /**
189   Shutdown our hardware
190 
191   DXE Core will disable interrupts and turn off the timer and disable interrupts
192   after all the event handlers have run.
193 
194   @param[in]  Event   The Event that is being processed
195   @param[in]  Context Event Context
196 **/
197 VOID
198 EFIAPI
GicV3ExitBootServicesEvent(IN EFI_EVENT Event,IN VOID * Context)199 GicV3ExitBootServicesEvent (
200   IN EFI_EVENT  Event,
201   IN VOID       *Context
202   )
203 {
204   UINTN    Index;
205 
206   // Acknowledge all pending interrupts
207   for (Index = 0; Index < mGicNumInterrupts; Index++) {
208     GicV3DisableInterruptSource (&gHardwareInterruptV3Protocol, Index);
209   }
210 
211   for (Index = 0; Index < mGicNumInterrupts; Index++) {
212     GicV3EndOfInterrupt (&gHardwareInterruptV3Protocol, Index);
213   }
214 
215   // Disable Gic Interface
216   ArmGicV3DisableInterruptInterface ();
217 
218   // Disable Gic Distributor
219   ArmGicDisableDistributor (mGicDistributorBase);
220 }
221 
222 /**
223   Initialize the state information for the CPU Architectural Protocol
224 
225   @param  ImageHandle   of the loaded driver
226   @param  SystemTable   Pointer to the System Table
227 
228   @retval EFI_SUCCESS           Protocol registered
229   @retval EFI_OUT_OF_RESOURCES  Cannot allocate protocol data structure
230   @retval EFI_DEVICE_ERROR      Hardware problems
231 
232 **/
233 EFI_STATUS
GicV3DxeInitialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)234 GicV3DxeInitialize (
235   IN EFI_HANDLE         ImageHandle,
236   IN EFI_SYSTEM_TABLE   *SystemTable
237   )
238 {
239   EFI_STATUS              Status;
240   UINTN                   Index;
241   UINT32                  RegOffset;
242   UINTN                   RegShift;
243   UINT64                  CpuTarget;
244   UINT64                  MpId;
245 
246   // Make sure the Interrupt Controller Protocol is not already installed in the system.
247   ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gHardwareInterruptProtocolGuid);
248 
249   mGicDistributorBase    = PcdGet32 (PcdGicDistributorBase);
250   mGicRedistributorsBase = PcdGet32 (PcdGicRedistributorsBase);
251   mGicNumInterrupts      = ArmGicGetMaxNumInterrupts (mGicDistributorBase);
252 
253   //
254   // We will be driving this GIC in native v3 mode, i.e., with Affinity
255   // Routing enabled. So ensure that the ARE bit is set.
256   //
257   if (!FeaturePcdGet (PcdArmGicV3WithV2Legacy)) {
258     MmioOr32 (mGicDistributorBase + ARM_GIC_ICDDCR, ARM_GIC_ICDDCR_ARE);
259   }
260 
261   for (Index = 0; Index < mGicNumInterrupts; Index++) {
262     GicV3DisableInterruptSource (&gHardwareInterruptV3Protocol, Index);
263 
264     // Set Priority
265     RegOffset = Index / 4;
266     RegShift = (Index % 4) * 8;
267     MmioAndThenOr32 (
268       mGicDistributorBase + ARM_GIC_ICDIPR + (4 * RegOffset),
269       ~(0xff << RegShift),
270       ARM_GIC_DEFAULT_PRIORITY << RegShift
271       );
272   }
273 
274   //
275   // Targets the interrupts to the Primary Cpu
276   //
277 
278   if (FeaturePcdGet (PcdArmGicV3WithV2Legacy)) {
279     // Only Primary CPU will run this code. We can identify our GIC CPU ID by reading
280     // the GIC Distributor Target register. The 8 first GICD_ITARGETSRn are banked to each
281     // connected CPU. These 8 registers hold the CPU targets fields for interrupts 0-31.
282     // More Info in the GIC Specification about "Interrupt Processor Targets Registers"
283     //
284     // Read the first Interrupt Processor Targets Register (that corresponds to the 4
285     // first SGIs)
286     CpuTarget = MmioRead32 (mGicDistributorBase + ARM_GIC_ICDIPTR);
287 
288     // The CPU target is a bit field mapping each CPU to a GIC CPU Interface. This value
289     // is 0 when we run on a uniprocessor platform.
290     if (CpuTarget != 0) {
291       // The 8 first Interrupt Processor Targets Registers are read-only
292       for (Index = 8; Index < (mGicNumInterrupts / 4); Index++) {
293         MmioWrite32 (mGicDistributorBase + ARM_GIC_ICDIPTR + (Index * 4), CpuTarget);
294       }
295     }
296   } else {
297     MpId = ArmReadMpidr ();
298     CpuTarget = MpId & (ARM_CORE_AFF0 | ARM_CORE_AFF1 | ARM_CORE_AFF2 | ARM_CORE_AFF3);
299 
300     // Route the SPIs to the primary CPU. SPIs start at the INTID 32
301     for (Index = 0; Index < (mGicNumInterrupts - 32); Index++) {
302       MmioWrite32 (mGicDistributorBase + ARM_GICD_IROUTER + (Index * 8), CpuTarget | ARM_GICD_IROUTER_IRM);
303     }
304   }
305 
306   // Set binary point reg to 0x7 (no preemption)
307   ArmGicV3SetBinaryPointer (0x7);
308 
309   // Set priority mask reg to 0xff to allow all priorities through
310   ArmGicV3SetPriorityMask (0xff);
311 
312   // Enable gic cpu interface
313   ArmGicV3EnableInterruptInterface ();
314 
315   // Enable gic distributor
316   ArmGicEnableDistributor (mGicDistributorBase);
317 
318   Status = InstallAndRegisterInterruptService (
319           &gHardwareInterruptV3Protocol, GicV3IrqInterruptHandler, GicV3ExitBootServicesEvent);
320 
321   return Status;
322 }
323