Home | History | Annotate | Download | only in GicV2
      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
     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
     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
    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
    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
    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
    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
    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