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