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-2015, 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   }
182 
183   GicV2EndOfInterrupt (&gHardwareInterruptV2Protocol, GicInterrupt);
184 }
185 
186 //
187 // The protocol instance produced by this driver
188 //
189 EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV2Protocol = {
190   RegisterInterruptSource,
191   GicV2EnableInterruptSource,
192   GicV2DisableInterruptSource,
193   GicV2GetInterruptSourceState,
194   GicV2EndOfInterrupt
195 };
196 
197 /**
198   Shutdown our hardware
199 
200   DXE Core will disable interrupts and turn off the timer and disable interrupts
201   after all the event handlers have run.
202 
203   @param[in]  Event   The Event that is being processed
204   @param[in]  Context Event Context
205 **/
206 VOID
207 EFIAPI
GicV2ExitBootServicesEvent(IN EFI_EVENT Event,IN VOID * Context)208 GicV2ExitBootServicesEvent (
209   IN EFI_EVENT  Event,
210   IN VOID       *Context
211   )
212 {
213   UINTN    Index;
214   UINT32   GicInterrupt;
215 
216   // Disable all the interrupts
217   for (Index = 0; Index < mGicNumInterrupts; Index++) {
218     GicV2DisableInterruptSource (&gHardwareInterruptV2Protocol, Index);
219   }
220 
221   // Acknowledge all pending interrupts
222   do {
223     GicInterrupt = ArmGicV2AcknowledgeInterrupt (mGicInterruptInterfaceBase);
224 
225     if ((GicInterrupt & ARM_GIC_ICCIAR_ACKINTID) < mGicNumInterrupts) {
226       GicV2EndOfInterrupt (&gHardwareInterruptV2Protocol, GicInterrupt);
227     }
228   } while (!ARM_GIC_IS_SPECIAL_INTERRUPTS (GicInterrupt));
229 
230   // Disable Gic Interface
231   ArmGicV2DisableInterruptInterface (mGicInterruptInterfaceBase);
232 
233   // Disable Gic Distributor
234   ArmGicDisableDistributor (mGicDistributorBase);
235 }
236 
237 /**
238   Initialize the state information for the CPU Architectural Protocol
239 
240   @param  ImageHandle   of the loaded driver
241   @param  SystemTable   Pointer to the System Table
242 
243   @retval EFI_SUCCESS           Protocol registered
244   @retval EFI_OUT_OF_RESOURCES  Cannot allocate protocol data structure
245   @retval EFI_DEVICE_ERROR      Hardware problems
246 
247 **/
248 EFI_STATUS
GicV2DxeInitialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)249 GicV2DxeInitialize (
250   IN EFI_HANDLE         ImageHandle,
251   IN EFI_SYSTEM_TABLE   *SystemTable
252   )
253 {
254   EFI_STATUS              Status;
255   UINTN                   Index;
256   UINT32                  RegOffset;
257   UINTN                   RegShift;
258   UINT32                  CpuTarget;
259 
260   // Make sure the Interrupt Controller Protocol is not already installed in the system.
261   ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gHardwareInterruptProtocolGuid);
262 
263   mGicInterruptInterfaceBase = PcdGet32 (PcdGicInterruptInterfaceBase);
264   mGicDistributorBase = PcdGet32 (PcdGicDistributorBase);
265   mGicNumInterrupts = ArmGicGetMaxNumInterrupts (mGicDistributorBase);
266 
267   for (Index = 0; Index < mGicNumInterrupts; Index++) {
268     GicV2DisableInterruptSource (&gHardwareInterruptV2Protocol, Index);
269 
270     // Set Priority
271     RegOffset = Index / 4;
272     RegShift = (Index % 4) * 8;
273     MmioAndThenOr32 (
274       mGicDistributorBase + ARM_GIC_ICDIPR + (4 * RegOffset),
275       ~(0xff << RegShift),
276       ARM_GIC_DEFAULT_PRIORITY << RegShift
277       );
278   }
279 
280   //
281   // Targets the interrupts to the Primary Cpu
282   //
283 
284   // Only Primary CPU will run this code. We can identify our GIC CPU ID by reading
285   // the GIC Distributor Target register. The 8 first GICD_ITARGETSRn are banked to each
286   // connected CPU. These 8 registers hold the CPU targets fields for interrupts 0-31.
287   // More Info in the GIC Specification about "Interrupt Processor Targets Registers"
288   //
289   // Read the first Interrupt Processor Targets Register (that corresponds to the 4
290   // first SGIs)
291   CpuTarget = MmioRead32 (mGicDistributorBase + ARM_GIC_ICDIPTR);
292 
293   // The CPU target is a bit field mapping each CPU to a GIC CPU Interface. This value
294   // is 0 when we run on a uniprocessor platform.
295   if (CpuTarget != 0) {
296     // The 8 first Interrupt Processor Targets Registers are read-only
297     for (Index = 8; Index < (mGicNumInterrupts / 4); Index++) {
298       MmioWrite32 (mGicDistributorBase + ARM_GIC_ICDIPTR + (Index * 4), CpuTarget);
299     }
300   }
301 
302   // Set binary point reg to 0x7 (no preemption)
303   MmioWrite32 (mGicInterruptInterfaceBase + ARM_GIC_ICCBPR, 0x7);
304 
305   // Set priority mask reg to 0xff to allow all priorities through
306   MmioWrite32 (mGicInterruptInterfaceBase + ARM_GIC_ICCPMR, 0xff);
307 
308   // Enable gic cpu interface
309   ArmGicEnableInterruptInterface (mGicInterruptInterfaceBase);
310 
311   // Enable gic distributor
312   ArmGicEnableDistributor (mGicDistributorBase);
313 
314   Status = InstallAndRegisterInterruptService (
315           &gHardwareInterruptV2Protocol, GicV2IrqInterruptHandler, GicV2ExitBootServicesEvent);
316 
317   return Status;
318 }
319