11e57a46299244793beb27e74be171d1540606999oliviermartin/** @file
21e57a46299244793beb27e74be171d1540606999oliviermartin  Timer Architecture Protocol driver of the ARM flavor
31e57a46299244793beb27e74be171d1540606999oliviermartin
4e703b085f6a3a2e7a61be97d9154437520b3ae75Olivier Martin  Copyright (c) 2011-2013 ARM Ltd. All rights reserved.<BR>
5e703b085f6a3a2e7a61be97d9154437520b3ae75Olivier Martin
6e703b085f6a3a2e7a61be97d9154437520b3ae75Olivier Martin  This program and the accompanying materials
7e703b085f6a3a2e7a61be97d9154437520b3ae75Olivier Martin  are licensed and made available under the terms and conditions of the BSD License
8e703b085f6a3a2e7a61be97d9154437520b3ae75Olivier Martin  which accompanies this distribution.  The full text of the license may be found at
9e703b085f6a3a2e7a61be97d9154437520b3ae75Olivier Martin  http://opensource.org/licenses/bsd-license.php
101e57a46299244793beb27e74be171d1540606999oliviermartin
11e703b085f6a3a2e7a61be97d9154437520b3ae75Olivier Martin  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12e703b085f6a3a2e7a61be97d9154437520b3ae75Olivier Martin  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
131e57a46299244793beb27e74be171d1540606999oliviermartin
141e57a46299244793beb27e74be171d1540606999oliviermartin**/
151e57a46299244793beb27e74be171d1540606999oliviermartin
161e57a46299244793beb27e74be171d1540606999oliviermartin
171e57a46299244793beb27e74be171d1540606999oliviermartin#include <PiDxe.h>
181e57a46299244793beb27e74be171d1540606999oliviermartin
191e57a46299244793beb27e74be171d1540606999oliviermartin#include <Library/ArmLib.h>
201e57a46299244793beb27e74be171d1540606999oliviermartin#include <Library/BaseLib.h>
211e57a46299244793beb27e74be171d1540606999oliviermartin#include <Library/DebugLib.h>
221e57a46299244793beb27e74be171d1540606999oliviermartin#include <Library/BaseMemoryLib.h>
231e57a46299244793beb27e74be171d1540606999oliviermartin#include <Library/UefiBootServicesTableLib.h>
241e57a46299244793beb27e74be171d1540606999oliviermartin#include <Library/UefiLib.h>
251e57a46299244793beb27e74be171d1540606999oliviermartin#include <Library/PcdLib.h>
261e57a46299244793beb27e74be171d1540606999oliviermartin#include <Library/IoLib.h>
274f6d34b434c0f063c68bdd4445da9097358b9afcArd Biesheuvel#include <Library/ArmGenericTimerCounterLib.h>
28d4bb43cee15895da3d53009396f1a53aae15c056Ard Biesheuvel#include <Library/ArmArchTimer.h>
291e57a46299244793beb27e74be171d1540606999oliviermartin
301e57a46299244793beb27e74be171d1540606999oliviermartin#include <Protocol/Timer.h>
311e57a46299244793beb27e74be171d1540606999oliviermartin#include <Protocol/HardwareInterrupt.h>
321e57a46299244793beb27e74be171d1540606999oliviermartin
331e57a46299244793beb27e74be171d1540606999oliviermartin// The notification function to call on every timer interrupt.
341e57a46299244793beb27e74be171d1540606999oliviermartinEFI_TIMER_NOTIFY      mTimerNotifyFunction     = (EFI_TIMER_NOTIFY)NULL;
351e57a46299244793beb27e74be171d1540606999oliviermartinEFI_EVENT             EfiExitBootServicesEvent = (EFI_EVENT)NULL;
361e57a46299244793beb27e74be171d1540606999oliviermartin
371e57a46299244793beb27e74be171d1540606999oliviermartin// The current period of the timer interrupt
381e57a46299244793beb27e74be171d1540606999oliviermartinUINT64 mTimerPeriod = 0;
39c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin// The latest Timer Tick calculated for mTimerPeriod
40c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier MartinUINT64 mTimerTicks = 0;
41c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin// Number of elapsed period since the last Timer interrupt
42c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier MartinUINT64 mElapsedPeriod = 1;
431e57a46299244793beb27e74be171d1540606999oliviermartin
441e57a46299244793beb27e74be171d1540606999oliviermartin// Cached copy of the Hardware Interrupt protocol instance
451e57a46299244793beb27e74be171d1540606999oliviermartinEFI_HARDWARE_INTERRUPT_PROTOCOL *gInterrupt = NULL;
461e57a46299244793beb27e74be171d1540606999oliviermartin
471e57a46299244793beb27e74be171d1540606999oliviermartin/**
483402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  This function registers the handler NotifyFunction so it is called every time
493402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  the timer interrupt fires.  It also passes the amount of time since the last
503402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  handler call to the NotifyFunction.  If NotifyFunction is NULL, then the
513402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  handler is unregistered.  If the handler is registered, then EFI_SUCCESS is
523402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  returned.  If the CPU does not support registering a timer interrupt handler,
533402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  then EFI_UNSUPPORTED is returned.  If an attempt is made to register a handler
543402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  when a handler is already registered, then EFI_ALREADY_STARTED is returned.
553402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  If an attempt is made to unregister a handler when a handler is not registered,
563402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  then EFI_INVALID_PARAMETER is returned.  If an error occurs attempting to
573402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  register the NotifyFunction with the timer interrupt, then EFI_DEVICE_ERROR
581e57a46299244793beb27e74be171d1540606999oliviermartin  is returned.
591e57a46299244793beb27e74be171d1540606999oliviermartin
601e57a46299244793beb27e74be171d1540606999oliviermartin  @param  This             The EFI_TIMER_ARCH_PROTOCOL instance.
611e57a46299244793beb27e74be171d1540606999oliviermartin  @param  NotifyFunction   The function to call when a timer interrupt fires. This
621e57a46299244793beb27e74be171d1540606999oliviermartin                           function executes at TPL_HIGH_LEVEL. The DXE Core will
631e57a46299244793beb27e74be171d1540606999oliviermartin                           register a handler for the timer interrupt, so it can know
641e57a46299244793beb27e74be171d1540606999oliviermartin                           how much time has passed. This information is used to
651e57a46299244793beb27e74be171d1540606999oliviermartin                           signal timer based events. NULL will unregister the handler.
661e57a46299244793beb27e74be171d1540606999oliviermartin  @retval EFI_SUCCESS           The timer handler was registered.
671e57a46299244793beb27e74be171d1540606999oliviermartin  @retval EFI_UNSUPPORTED       The platform does not support timer interrupts.
681e57a46299244793beb27e74be171d1540606999oliviermartin  @retval EFI_ALREADY_STARTED   NotifyFunction is not NULL, and a handler is already
691e57a46299244793beb27e74be171d1540606999oliviermartin                                registered.
701e57a46299244793beb27e74be171d1540606999oliviermartin  @retval EFI_INVALID_PARAMETER NotifyFunction is NULL, and a handler was not
711e57a46299244793beb27e74be171d1540606999oliviermartin                                previously registered.
721e57a46299244793beb27e74be171d1540606999oliviermartin  @retval EFI_DEVICE_ERROR      The timer handler could not be registered.
731e57a46299244793beb27e74be171d1540606999oliviermartin
741e57a46299244793beb27e74be171d1540606999oliviermartin**/
751e57a46299244793beb27e74be171d1540606999oliviermartinEFI_STATUS
761e57a46299244793beb27e74be171d1540606999oliviermartinEFIAPI
771e57a46299244793beb27e74be171d1540606999oliviermartinTimerDriverRegisterHandler (
781e57a46299244793beb27e74be171d1540606999oliviermartin  IN EFI_TIMER_ARCH_PROTOCOL  *This,
791e57a46299244793beb27e74be171d1540606999oliviermartin  IN EFI_TIMER_NOTIFY         NotifyFunction
801e57a46299244793beb27e74be171d1540606999oliviermartin  )
811e57a46299244793beb27e74be171d1540606999oliviermartin{
821e57a46299244793beb27e74be171d1540606999oliviermartin  if ((NotifyFunction == NULL) && (mTimerNotifyFunction == NULL)) {
831e57a46299244793beb27e74be171d1540606999oliviermartin    return EFI_INVALID_PARAMETER;
841e57a46299244793beb27e74be171d1540606999oliviermartin  }
851e57a46299244793beb27e74be171d1540606999oliviermartin
861e57a46299244793beb27e74be171d1540606999oliviermartin  if ((NotifyFunction != NULL) && (mTimerNotifyFunction != NULL)) {
871e57a46299244793beb27e74be171d1540606999oliviermartin    return EFI_ALREADY_STARTED;
881e57a46299244793beb27e74be171d1540606999oliviermartin  }
891e57a46299244793beb27e74be171d1540606999oliviermartin
901e57a46299244793beb27e74be171d1540606999oliviermartin  mTimerNotifyFunction = NotifyFunction;
911e57a46299244793beb27e74be171d1540606999oliviermartin
921e57a46299244793beb27e74be171d1540606999oliviermartin  return EFI_SUCCESS;
931e57a46299244793beb27e74be171d1540606999oliviermartin}
941e57a46299244793beb27e74be171d1540606999oliviermartin
951e57a46299244793beb27e74be171d1540606999oliviermartin/**
961e57a46299244793beb27e74be171d1540606999oliviermartin    Disable the timer
971e57a46299244793beb27e74be171d1540606999oliviermartin**/
981e57a46299244793beb27e74be171d1540606999oliviermartinVOID
991e57a46299244793beb27e74be171d1540606999oliviermartinEFIAPI
1001e57a46299244793beb27e74be171d1540606999oliviermartinExitBootServicesEvent (
1011e57a46299244793beb27e74be171d1540606999oliviermartin  IN EFI_EVENT  Event,
1021e57a46299244793beb27e74be171d1540606999oliviermartin  IN VOID       *Context
1031e57a46299244793beb27e74be171d1540606999oliviermartin  )
1041e57a46299244793beb27e74be171d1540606999oliviermartin{
1054f6d34b434c0f063c68bdd4445da9097358b9afcArd Biesheuvel  ArmGenericTimerDisableTimer ();
1061e57a46299244793beb27e74be171d1540606999oliviermartin}
1071e57a46299244793beb27e74be171d1540606999oliviermartin
1081e57a46299244793beb27e74be171d1540606999oliviermartin/**
1091e57a46299244793beb27e74be171d1540606999oliviermartin
1103402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  This function adjusts the period of timer interrupts to the value specified
1113402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  by TimerPeriod.  If the timer period is updated, then the selected timer
1123402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned.  If
1133402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  the timer hardware is not programmable, then EFI_UNSUPPORTED is returned.
1143402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  If an error occurs while attempting to update the timer period, then the
1153402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  timer hardware will be put back in its state prior to this call, and
1163402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  EFI_DEVICE_ERROR is returned.  If TimerPeriod is 0, then the timer interrupt
1173402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  is disabled.  This is not the same as disabling the CPU's interrupts.
1183402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  Instead, it must either turn off the timer hardware, or it must adjust the
1193402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  interrupt controller so that a CPU interrupt is not generated when the timer
1203402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  interrupt fires.
1211e57a46299244793beb27e74be171d1540606999oliviermartin
1221e57a46299244793beb27e74be171d1540606999oliviermartin  @param  This             The EFI_TIMER_ARCH_PROTOCOL instance.
1231e57a46299244793beb27e74be171d1540606999oliviermartin  @param  TimerPeriod      The rate to program the timer interrupt in 100 nS units. If
1241e57a46299244793beb27e74be171d1540606999oliviermartin                           the timer hardware is not programmable, then EFI_UNSUPPORTED is
1251e57a46299244793beb27e74be171d1540606999oliviermartin                           returned. If the timer is programmable, then the timer period
1261e57a46299244793beb27e74be171d1540606999oliviermartin                           will be rounded up to the nearest timer period that is supported
1271e57a46299244793beb27e74be171d1540606999oliviermartin                           by the timer hardware. If TimerPeriod is set to 0, then the
1281e57a46299244793beb27e74be171d1540606999oliviermartin                           timer interrupts will be disabled.
1291e57a46299244793beb27e74be171d1540606999oliviermartin
1301e57a46299244793beb27e74be171d1540606999oliviermartin
1311e57a46299244793beb27e74be171d1540606999oliviermartin  @retval EFI_SUCCESS           The timer period was changed.
1321e57a46299244793beb27e74be171d1540606999oliviermartin  @retval EFI_UNSUPPORTED       The platform cannot change the period of the timer interrupt.
1331e57a46299244793beb27e74be171d1540606999oliviermartin  @retval EFI_DEVICE_ERROR      The timer period could not be changed due to a device error.
1341e57a46299244793beb27e74be171d1540606999oliviermartin
1351e57a46299244793beb27e74be171d1540606999oliviermartin**/
1361e57a46299244793beb27e74be171d1540606999oliviermartinEFI_STATUS
1371e57a46299244793beb27e74be171d1540606999oliviermartinEFIAPI
1381e57a46299244793beb27e74be171d1540606999oliviermartinTimerDriverSetTimerPeriod (
1391e57a46299244793beb27e74be171d1540606999oliviermartin  IN EFI_TIMER_ARCH_PROTOCOL  *This,
1401e57a46299244793beb27e74be171d1540606999oliviermartin  IN UINT64                   TimerPeriod
1411e57a46299244793beb27e74be171d1540606999oliviermartin  )
1421e57a46299244793beb27e74be171d1540606999oliviermartin{
143c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin  UINT64      CounterValue;
1441e57a46299244793beb27e74be171d1540606999oliviermartin  UINT64      TimerTicks;
145c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin  EFI_TPL     OriginalTPL;
1463402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron
1471e57a46299244793beb27e74be171d1540606999oliviermartin  // Always disable the timer
1484f6d34b434c0f063c68bdd4445da9097358b9afcArd Biesheuvel  ArmGenericTimerDisableTimer ();
1491e57a46299244793beb27e74be171d1540606999oliviermartin
1501e57a46299244793beb27e74be171d1540606999oliviermartin  if (TimerPeriod != 0) {
151c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    // mTimerTicks = TimerPeriod in 1ms unit x Frequency.10^-3
152c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    //             = TimerPeriod.10^-4 x Frequency.10^-3
153c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    //             = (TimerPeriod x Frequency) x 10^-7
1547a1e861e2925b9c006bcf8f95bd0d720a8c48328Ard Biesheuvel    TimerTicks = MultU64x32 (TimerPeriod, ArmGenericTimerGetTimerFreq ());
15533292af5f13b18ef5124f32a7bbf0b05b2d519c5Olivier Martin    TimerTicks = DivU64x32 (TimerTicks, 10000000U);
1561e57a46299244793beb27e74be171d1540606999oliviermartin
157c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    // Raise TPL to update the mTimerTicks and mTimerPeriod to ensure these values
158c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    // are coherent in the interrupt handler
159c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);
160c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin
161c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    mTimerTicks    = TimerTicks;
162c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    mTimerPeriod   = TimerPeriod;
163c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    mElapsedPeriod = 1;
164c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin
165c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    gBS->RestoreTPL (OriginalTPL);
166c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin
1674f6d34b434c0f063c68bdd4445da9097358b9afcArd Biesheuvel    // Get value of the current timer
1684f6d34b434c0f063c68bdd4445da9097358b9afcArd Biesheuvel    CounterValue = ArmGenericTimerGetSystemCount ();
169c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    // Set the interrupt in Current Time + mTimerTick
1704f6d34b434c0f063c68bdd4445da9097358b9afcArd Biesheuvel    ArmGenericTimerSetCompareVal (CounterValue + mTimerTicks);
1711e57a46299244793beb27e74be171d1540606999oliviermartin
1721e57a46299244793beb27e74be171d1540606999oliviermartin    // Enable the timer
1734f6d34b434c0f063c68bdd4445da9097358b9afcArd Biesheuvel    ArmGenericTimerEnableTimer ();
174c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin  } else {
175c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    // Save the new timer period
176c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    mTimerPeriod   = TimerPeriod;
177c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    // Reset the elapsed period
178c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    mElapsedPeriod = 1;
1791e57a46299244793beb27e74be171d1540606999oliviermartin  }
1801e57a46299244793beb27e74be171d1540606999oliviermartin
1811e57a46299244793beb27e74be171d1540606999oliviermartin  return EFI_SUCCESS;
1821e57a46299244793beb27e74be171d1540606999oliviermartin}
1831e57a46299244793beb27e74be171d1540606999oliviermartin
1841e57a46299244793beb27e74be171d1540606999oliviermartin/**
1853402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  This function retrieves the period of timer interrupts in 100 ns units,
1863402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  returns that value in TimerPeriod, and returns EFI_SUCCESS.  If TimerPeriod
1873402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  is NULL, then EFI_INVALID_PARAMETER is returned.  If a TimerPeriod of 0 is
1881e57a46299244793beb27e74be171d1540606999oliviermartin  returned, then the timer is currently disabled.
1891e57a46299244793beb27e74be171d1540606999oliviermartin
1901e57a46299244793beb27e74be171d1540606999oliviermartin  @param  This             The EFI_TIMER_ARCH_PROTOCOL instance.
1911e57a46299244793beb27e74be171d1540606999oliviermartin  @param  TimerPeriod      A pointer to the timer period to retrieve in 100 ns units. If
1921e57a46299244793beb27e74be171d1540606999oliviermartin                           0 is returned, then the timer is currently disabled.
1931e57a46299244793beb27e74be171d1540606999oliviermartin
1941e57a46299244793beb27e74be171d1540606999oliviermartin
1951e57a46299244793beb27e74be171d1540606999oliviermartin  @retval EFI_SUCCESS           The timer period was returned in TimerPeriod.
1961e57a46299244793beb27e74be171d1540606999oliviermartin  @retval EFI_INVALID_PARAMETER TimerPeriod is NULL.
1971e57a46299244793beb27e74be171d1540606999oliviermartin
1981e57a46299244793beb27e74be171d1540606999oliviermartin**/
1991e57a46299244793beb27e74be171d1540606999oliviermartinEFI_STATUS
2001e57a46299244793beb27e74be171d1540606999oliviermartinEFIAPI
2011e57a46299244793beb27e74be171d1540606999oliviermartinTimerDriverGetTimerPeriod (
2021e57a46299244793beb27e74be171d1540606999oliviermartin  IN EFI_TIMER_ARCH_PROTOCOL   *This,
2031e57a46299244793beb27e74be171d1540606999oliviermartin  OUT UINT64                   *TimerPeriod
2041e57a46299244793beb27e74be171d1540606999oliviermartin  )
2051e57a46299244793beb27e74be171d1540606999oliviermartin{
2061e57a46299244793beb27e74be171d1540606999oliviermartin  if (TimerPeriod == NULL) {
2071e57a46299244793beb27e74be171d1540606999oliviermartin    return EFI_INVALID_PARAMETER;
2081e57a46299244793beb27e74be171d1540606999oliviermartin  }
2091e57a46299244793beb27e74be171d1540606999oliviermartin
2101e57a46299244793beb27e74be171d1540606999oliviermartin  *TimerPeriod = mTimerPeriod;
2111e57a46299244793beb27e74be171d1540606999oliviermartin  return EFI_SUCCESS;
2121e57a46299244793beb27e74be171d1540606999oliviermartin}
2131e57a46299244793beb27e74be171d1540606999oliviermartin
2141e57a46299244793beb27e74be171d1540606999oliviermartin/**
2153402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  This function generates a soft timer interrupt. If the platform does not support soft
2163402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned.
2173402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler()
2183402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  service, then a soft timer interrupt will be generated. If the timer interrupt is
2193402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  enabled when this service is called, then the registered handler will be invoked. The
2203402aac7d985bf8a9f9d3c639f3fe93609380513Ronald Cron  registered handler should not be able to distinguish a hardware-generated timer
2211e57a46299244793beb27e74be171d1540606999oliviermartin  interrupt from a software-generated timer interrupt.
2221e57a46299244793beb27e74be171d1540606999oliviermartin
2231e57a46299244793beb27e74be171d1540606999oliviermartin  @param  This             The EFI_TIMER_ARCH_PROTOCOL instance.
2241e57a46299244793beb27e74be171d1540606999oliviermartin
2251e57a46299244793beb27e74be171d1540606999oliviermartin  @retval EFI_SUCCESS           The soft timer interrupt was generated.
2261e57a46299244793beb27e74be171d1540606999oliviermartin  @retval EFI_UNSUPPORTED       The platform does not support the generation of soft timer interrupts.
2271e57a46299244793beb27e74be171d1540606999oliviermartin
2281e57a46299244793beb27e74be171d1540606999oliviermartin**/
2291e57a46299244793beb27e74be171d1540606999oliviermartinEFI_STATUS
2301e57a46299244793beb27e74be171d1540606999oliviermartinEFIAPI
2311e57a46299244793beb27e74be171d1540606999oliviermartinTimerDriverGenerateSoftInterrupt (
2321e57a46299244793beb27e74be171d1540606999oliviermartin  IN EFI_TIMER_ARCH_PROTOCOL  *This
2331e57a46299244793beb27e74be171d1540606999oliviermartin  )
2341e57a46299244793beb27e74be171d1540606999oliviermartin{
2351e57a46299244793beb27e74be171d1540606999oliviermartin  return EFI_UNSUPPORTED;
2361e57a46299244793beb27e74be171d1540606999oliviermartin}
2371e57a46299244793beb27e74be171d1540606999oliviermartin
2381e57a46299244793beb27e74be171d1540606999oliviermartin/**
2391e57a46299244793beb27e74be171d1540606999oliviermartin  Interface structure for the Timer Architectural Protocol.
2401e57a46299244793beb27e74be171d1540606999oliviermartin
2411e57a46299244793beb27e74be171d1540606999oliviermartin  @par Protocol Description:
2421e57a46299244793beb27e74be171d1540606999oliviermartin  This protocol provides the services to initialize a periodic timer
2431e57a46299244793beb27e74be171d1540606999oliviermartin  interrupt, and to register a handler that is called each time the timer
2441e57a46299244793beb27e74be171d1540606999oliviermartin  interrupt fires.  It may also provide a service to adjust the rate of the
2451e57a46299244793beb27e74be171d1540606999oliviermartin  periodic timer interrupt.  When a timer interrupt occurs, the handler is
2461e57a46299244793beb27e74be171d1540606999oliviermartin  passed the amount of time that has passed since the previous timer
2471e57a46299244793beb27e74be171d1540606999oliviermartin  interrupt.
2481e57a46299244793beb27e74be171d1540606999oliviermartin
2491e57a46299244793beb27e74be171d1540606999oliviermartin  @param RegisterHandler
2501e57a46299244793beb27e74be171d1540606999oliviermartin  Registers a handler that will be called each time the
2511e57a46299244793beb27e74be171d1540606999oliviermartin  timer interrupt fires.  TimerPeriod defines the minimum
2521e57a46299244793beb27e74be171d1540606999oliviermartin  time between timer interrupts, so TimerPeriod will also
2531e57a46299244793beb27e74be171d1540606999oliviermartin  be the minimum time between calls to the registered
2541e57a46299244793beb27e74be171d1540606999oliviermartin  handler.
2551e57a46299244793beb27e74be171d1540606999oliviermartin
2561e57a46299244793beb27e74be171d1540606999oliviermartin  @param SetTimerPeriod
2571e57a46299244793beb27e74be171d1540606999oliviermartin  Sets the period of the timer interrupt in 100 nS units.
2581e57a46299244793beb27e74be171d1540606999oliviermartin  This function is optional, and may return EFI_UNSUPPORTED.
2591e57a46299244793beb27e74be171d1540606999oliviermartin  If this function is supported, then the timer period will
2601e57a46299244793beb27e74be171d1540606999oliviermartin  be rounded up to the nearest supported timer period.
2611e57a46299244793beb27e74be171d1540606999oliviermartin
2621e57a46299244793beb27e74be171d1540606999oliviermartin
2631e57a46299244793beb27e74be171d1540606999oliviermartin  @param GetTimerPeriod
2641e57a46299244793beb27e74be171d1540606999oliviermartin  Retrieves the period of the timer interrupt in 100 nS units.
2651e57a46299244793beb27e74be171d1540606999oliviermartin
2661e57a46299244793beb27e74be171d1540606999oliviermartin  @param GenerateSoftInterrupt
2671e57a46299244793beb27e74be171d1540606999oliviermartin  Generates a soft timer interrupt that simulates the firing of
2681e57a46299244793beb27e74be171d1540606999oliviermartin  the timer interrupt. This service can be used to invoke the   registered handler if the timer interrupt has been masked for
2691e57a46299244793beb27e74be171d1540606999oliviermartin  a period of time.
2701e57a46299244793beb27e74be171d1540606999oliviermartin
2711e57a46299244793beb27e74be171d1540606999oliviermartin**/
2721e57a46299244793beb27e74be171d1540606999oliviermartinEFI_TIMER_ARCH_PROTOCOL   gTimer = {
2731e57a46299244793beb27e74be171d1540606999oliviermartin  TimerDriverRegisterHandler,
2741e57a46299244793beb27e74be171d1540606999oliviermartin  TimerDriverSetTimerPeriod,
2751e57a46299244793beb27e74be171d1540606999oliviermartin  TimerDriverGetTimerPeriod,
2761e57a46299244793beb27e74be171d1540606999oliviermartin  TimerDriverGenerateSoftInterrupt
2771e57a46299244793beb27e74be171d1540606999oliviermartin};
2781e57a46299244793beb27e74be171d1540606999oliviermartin
2791e57a46299244793beb27e74be171d1540606999oliviermartin/**
2801e57a46299244793beb27e74be171d1540606999oliviermartin
2811e57a46299244793beb27e74be171d1540606999oliviermartin  C Interrupt Handler called in the interrupt context when Source interrupt is active.
2821e57a46299244793beb27e74be171d1540606999oliviermartin
2831e57a46299244793beb27e74be171d1540606999oliviermartin
2841e57a46299244793beb27e74be171d1540606999oliviermartin  @param Source         Source of the interrupt. Hardware routing off a specific platform defines
2851e57a46299244793beb27e74be171d1540606999oliviermartin                        what source means.
2861e57a46299244793beb27e74be171d1540606999oliviermartin
2871e57a46299244793beb27e74be171d1540606999oliviermartin  @param SystemContext  Pointer to system register context. Mostly used by debuggers and will
2881e57a46299244793beb27e74be171d1540606999oliviermartin                        update the system context after the return from the interrupt if
2891e57a46299244793beb27e74be171d1540606999oliviermartin                        modified. Don't change these values unless you know what you are doing
2901e57a46299244793beb27e74be171d1540606999oliviermartin
2911e57a46299244793beb27e74be171d1540606999oliviermartin**/
2921e57a46299244793beb27e74be171d1540606999oliviermartinVOID
2931e57a46299244793beb27e74be171d1540606999oliviermartinEFIAPI
2941e57a46299244793beb27e74be171d1540606999oliviermartinTimerInterruptHandler (
2951e57a46299244793beb27e74be171d1540606999oliviermartin  IN  HARDWARE_INTERRUPT_SOURCE   Source,
2961e57a46299244793beb27e74be171d1540606999oliviermartin  IN  EFI_SYSTEM_CONTEXT          SystemContext
2971e57a46299244793beb27e74be171d1540606999oliviermartin  )
2981e57a46299244793beb27e74be171d1540606999oliviermartin{
2991e57a46299244793beb27e74be171d1540606999oliviermartin  EFI_TPL      OriginalTPL;
300c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin  UINT64       CurrentValue;
301c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin  UINT64       CompareValue;
3021e57a46299244793beb27e74be171d1540606999oliviermartin
3031e57a46299244793beb27e74be171d1540606999oliviermartin  //
3041e57a46299244793beb27e74be171d1540606999oliviermartin  // DXE core uses this callback for the EFI timer tick. The DXE core uses locks
3051e57a46299244793beb27e74be171d1540606999oliviermartin  // that raise to TPL_HIGH and then restore back to current level. Thus we need
3061e57a46299244793beb27e74be171d1540606999oliviermartin  // to make sure TPL level is set to TPL_HIGH while we are handling the timer tick.
3071e57a46299244793beb27e74be171d1540606999oliviermartin  //
3081e57a46299244793beb27e74be171d1540606999oliviermartin  OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);
3091e57a46299244793beb27e74be171d1540606999oliviermartin
3101e57a46299244793beb27e74be171d1540606999oliviermartin  // Check if the timer interrupt is active
3114f6d34b434c0f063c68bdd4445da9097358b9afcArd Biesheuvel  if ((ArmGenericTimerGetTimerCtrlReg () ) & ARM_ARCH_TIMER_ISTATUS) {
3121e57a46299244793beb27e74be171d1540606999oliviermartin
3131e57a46299244793beb27e74be171d1540606999oliviermartin    // Signal end of interrupt early to help avoid losing subsequent ticks from long duration handlers
3141e57a46299244793beb27e74be171d1540606999oliviermartin    gInterrupt->EndOfInterrupt (gInterrupt, Source);
3151e57a46299244793beb27e74be171d1540606999oliviermartin
3161e57a46299244793beb27e74be171d1540606999oliviermartin    if (mTimerNotifyFunction) {
317c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin      mTimerNotifyFunction (mTimerPeriod * mElapsedPeriod);
3181e57a46299244793beb27e74be171d1540606999oliviermartin    }
3191e57a46299244793beb27e74be171d1540606999oliviermartin
320c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    //
3211e57a46299244793beb27e74be171d1540606999oliviermartin    // Reload the Timer
322c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    //
323c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin
324c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    // Get current counter value
3254f6d34b434c0f063c68bdd4445da9097358b9afcArd Biesheuvel    CurrentValue = ArmGenericTimerGetSystemCount ();
326c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    // Get the counter value to compare with
3274f6d34b434c0f063c68bdd4445da9097358b9afcArd Biesheuvel    CompareValue = ArmGenericTimerGetCompareVal ();
328c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin
329c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    // This loop is needed in case we missed interrupts (eg: case when the interrupt handling
330c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    // has taken longer than mTickPeriod).
331c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    // Note: Physical Counter is counting up
332c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    mElapsedPeriod = 0;
333c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    do {
334c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin      CompareValue += mTimerTicks;
335c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin      mElapsedPeriod++;
336c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    } while (CompareValue < CurrentValue);
337c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin
338c6c4df80de577bdf301363d83bc74dc0453f1ca9Olivier Martin    // Set next compare value
3394f6d34b434c0f063c68bdd4445da9097358b9afcArd Biesheuvel    ArmGenericTimerSetCompareVal (CompareValue);
340b1a633434ddc5fc28de817debd963f7845fb78c7Ard Biesheuvel    ArmGenericTimerEnableTimer ();
3411e57a46299244793beb27e74be171d1540606999oliviermartin  }
3421e57a46299244793beb27e74be171d1540606999oliviermartin
3431e57a46299244793beb27e74be171d1540606999oliviermartin  // Enable timer interrupts
3441e57a46299244793beb27e74be171d1540606999oliviermartin  gInterrupt->EnableInterruptSource (gInterrupt, Source);
3451e57a46299244793beb27e74be171d1540606999oliviermartin
3461e57a46299244793beb27e74be171d1540606999oliviermartin  gBS->RestoreTPL (OriginalTPL);
3471e57a46299244793beb27e74be171d1540606999oliviermartin}
3481e57a46299244793beb27e74be171d1540606999oliviermartin
3491e57a46299244793beb27e74be171d1540606999oliviermartin
3501e57a46299244793beb27e74be171d1540606999oliviermartin/**
3511e57a46299244793beb27e74be171d1540606999oliviermartin  Initialize the state information for the Timer Architectural Protocol and
3521e57a46299244793beb27e74be171d1540606999oliviermartin  the Timer Debug support protocol that allows the debugger to break into a
3531e57a46299244793beb27e74be171d1540606999oliviermartin  running program.
3541e57a46299244793beb27e74be171d1540606999oliviermartin
3551e57a46299244793beb27e74be171d1540606999oliviermartin  @param  ImageHandle   of the loaded driver
3561e57a46299244793beb27e74be171d1540606999oliviermartin  @param  SystemTable   Pointer to the System Table
3571e57a46299244793beb27e74be171d1540606999oliviermartin
3581e57a46299244793beb27e74be171d1540606999oliviermartin  @retval EFI_SUCCESS           Protocol registered
3591e57a46299244793beb27e74be171d1540606999oliviermartin  @retval EFI_OUT_OF_RESOURCES  Cannot allocate protocol data structure
3601e57a46299244793beb27e74be171d1540606999oliviermartin  @retval EFI_DEVICE_ERROR      Hardware problems
3611e57a46299244793beb27e74be171d1540606999oliviermartin
3621e57a46299244793beb27e74be171d1540606999oliviermartin**/
3631e57a46299244793beb27e74be171d1540606999oliviermartinEFI_STATUS
3641e57a46299244793beb27e74be171d1540606999oliviermartinEFIAPI
3651e57a46299244793beb27e74be171d1540606999oliviermartinTimerInitialize (
3661e57a46299244793beb27e74be171d1540606999oliviermartin  IN EFI_HANDLE         ImageHandle,
3671e57a46299244793beb27e74be171d1540606999oliviermartin  IN EFI_SYSTEM_TABLE   *SystemTable
3681e57a46299244793beb27e74be171d1540606999oliviermartin  )
3691e57a46299244793beb27e74be171d1540606999oliviermartin{
3701e57a46299244793beb27e74be171d1540606999oliviermartin  EFI_HANDLE  Handle = NULL;
3711e57a46299244793beb27e74be171d1540606999oliviermartin  EFI_STATUS  Status;
372967efdcdc3a3a22550563acb9ec77f565b3dbee0Ard Biesheuvel  UINTN       TimerCtrlReg;
373967efdcdc3a3a22550563acb9ec77f565b3dbee0Ard Biesheuvel  UINT32      TimerHypIntrNum;
3741e57a46299244793beb27e74be171d1540606999oliviermartin
3751e57a46299244793beb27e74be171d1540606999oliviermartin  if (ArmIsArchTimerImplemented () == 0) {
3761e57a46299244793beb27e74be171d1540606999oliviermartin    DEBUG ((EFI_D_ERROR, "ARM Architectural Timer is not available in the CPU, hence cann't use this Driver \n"));
3771e57a46299244793beb27e74be171d1540606999oliviermartin    ASSERT (0);
3781e57a46299244793beb27e74be171d1540606999oliviermartin  }
3791e57a46299244793beb27e74be171d1540606999oliviermartin
3801e57a46299244793beb27e74be171d1540606999oliviermartin  // Find the interrupt controller protocol.  ASSERT if not found.
3811e57a46299244793beb27e74be171d1540606999oliviermartin  Status = gBS->LocateProtocol (&gHardwareInterruptProtocolGuid, NULL, (VOID **)&gInterrupt);
3821e57a46299244793beb27e74be171d1540606999oliviermartin  ASSERT_EFI_ERROR (Status);
3831e57a46299244793beb27e74be171d1540606999oliviermartin
3841e57a46299244793beb27e74be171d1540606999oliviermartin  // Disable the timer
3854f6d34b434c0f063c68bdd4445da9097358b9afcArd Biesheuvel  TimerCtrlReg = ArmGenericTimerGetTimerCtrlReg ();
386e703b085f6a3a2e7a61be97d9154437520b3ae75Olivier Martin  TimerCtrlReg |= ARM_ARCH_TIMER_IMASK;
387e703b085f6a3a2e7a61be97d9154437520b3ae75Olivier Martin  TimerCtrlReg &= ~ARM_ARCH_TIMER_ENABLE;
3884f6d34b434c0f063c68bdd4445da9097358b9afcArd Biesheuvel  ArmGenericTimerSetTimerCtrlReg (TimerCtrlReg);
3891e57a46299244793beb27e74be171d1540606999oliviermartin  Status = TimerDriverSetTimerPeriod (&gTimer, 0);
3901e57a46299244793beb27e74be171d1540606999oliviermartin  ASSERT_EFI_ERROR (Status);
3911e57a46299244793beb27e74be171d1540606999oliviermartin
3921e57a46299244793beb27e74be171d1540606999oliviermartin  // Install secure and Non-secure interrupt handlers
3931e57a46299244793beb27e74be171d1540606999oliviermartin  // Note: Because it is not possible to determine the security state of the
3941e57a46299244793beb27e74be171d1540606999oliviermartin  // CPU dynamically, we just install interrupt handler for both sec and non-sec
3951e57a46299244793beb27e74be171d1540606999oliviermartin  // timer PPI
3962785509b57d9ff92321c1083aab4a5ffc9519961Ard Biesheuvel  Status = gInterrupt->RegisterInterruptSource (gInterrupt, PcdGet32 (PcdArmArchTimerVirtIntrNum), TimerInterruptHandler);
3972785509b57d9ff92321c1083aab4a5ffc9519961Ard Biesheuvel  ASSERT_EFI_ERROR (Status);
3982785509b57d9ff92321c1083aab4a5ffc9519961Ard Biesheuvel
399967efdcdc3a3a22550563acb9ec77f565b3dbee0Ard Biesheuvel  //
400967efdcdc3a3a22550563acb9ec77f565b3dbee0Ard Biesheuvel  // The hypervisor timer interrupt may be omitted by implementations that
401967efdcdc3a3a22550563acb9ec77f565b3dbee0Ard Biesheuvel  // execute under virtualization.
402967efdcdc3a3a22550563acb9ec77f565b3dbee0Ard Biesheuvel  //
403967efdcdc3a3a22550563acb9ec77f565b3dbee0Ard Biesheuvel  TimerHypIntrNum = PcdGet32 (PcdArmArchTimerHypIntrNum);
404967efdcdc3a3a22550563acb9ec77f565b3dbee0Ard Biesheuvel  if (TimerHypIntrNum != 0) {
405967efdcdc3a3a22550563acb9ec77f565b3dbee0Ard Biesheuvel    Status = gInterrupt->RegisterInterruptSource (gInterrupt, TimerHypIntrNum, TimerInterruptHandler);
406967efdcdc3a3a22550563acb9ec77f565b3dbee0Ard Biesheuvel    ASSERT_EFI_ERROR (Status);
407967efdcdc3a3a22550563acb9ec77f565b3dbee0Ard Biesheuvel  }
4082785509b57d9ff92321c1083aab4a5ffc9519961Ard Biesheuvel
4091e57a46299244793beb27e74be171d1540606999oliviermartin  Status = gInterrupt->RegisterInterruptSource (gInterrupt, PcdGet32 (PcdArmArchTimerSecIntrNum), TimerInterruptHandler);
4101e57a46299244793beb27e74be171d1540606999oliviermartin  ASSERT_EFI_ERROR (Status);
4111e57a46299244793beb27e74be171d1540606999oliviermartin
4121e57a46299244793beb27e74be171d1540606999oliviermartin  Status = gInterrupt->RegisterInterruptSource (gInterrupt, PcdGet32 (PcdArmArchTimerIntrNum), TimerInterruptHandler);
4131e57a46299244793beb27e74be171d1540606999oliviermartin  ASSERT_EFI_ERROR (Status);
4141e57a46299244793beb27e74be171d1540606999oliviermartin
4151e57a46299244793beb27e74be171d1540606999oliviermartin  // Set up default timer
4161e57a46299244793beb27e74be171d1540606999oliviermartin  Status = TimerDriverSetTimerPeriod (&gTimer, FixedPcdGet32(PcdTimerPeriod)); // TIMER_DEFAULT_PERIOD
4171e57a46299244793beb27e74be171d1540606999oliviermartin  ASSERT_EFI_ERROR (Status);
4181e57a46299244793beb27e74be171d1540606999oliviermartin
4191e57a46299244793beb27e74be171d1540606999oliviermartin  // Install the Timer Architectural Protocol onto a new handle
4201e57a46299244793beb27e74be171d1540606999oliviermartin  Status = gBS->InstallMultipleProtocolInterfaces(
4211e57a46299244793beb27e74be171d1540606999oliviermartin                  &Handle,
4221e57a46299244793beb27e74be171d1540606999oliviermartin                  &gEfiTimerArchProtocolGuid,      &gTimer,
4231e57a46299244793beb27e74be171d1540606999oliviermartin                  NULL
4241e57a46299244793beb27e74be171d1540606999oliviermartin                  );
4251e57a46299244793beb27e74be171d1540606999oliviermartin  ASSERT_EFI_ERROR(Status);
4261e57a46299244793beb27e74be171d1540606999oliviermartin
427e703b085f6a3a2e7a61be97d9154437520b3ae75Olivier Martin  // Everything is ready, unmask and enable timer interrupts
428e703b085f6a3a2e7a61be97d9154437520b3ae75Olivier Martin  TimerCtrlReg = ARM_ARCH_TIMER_ENABLE;
4294f6d34b434c0f063c68bdd4445da9097358b9afcArd Biesheuvel  ArmGenericTimerSetTimerCtrlReg (TimerCtrlReg);
4301e57a46299244793beb27e74be171d1540606999oliviermartin
4311e57a46299244793beb27e74be171d1540606999oliviermartin  // Register for an ExitBootServicesEvent
4321e57a46299244793beb27e74be171d1540606999oliviermartin  Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_NOTIFY, ExitBootServicesEvent, NULL, &EfiExitBootServicesEvent);
4331e57a46299244793beb27e74be171d1540606999oliviermartin  ASSERT_EFI_ERROR (Status);
4341e57a46299244793beb27e74be171d1540606999oliviermartin
4351e57a46299244793beb27e74be171d1540606999oliviermartin  return Status;
4361e57a46299244793beb27e74be171d1540606999oliviermartin}
437