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