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