Trailing spaces create issue/warning when generating/applying patches. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Ronald Cron <ronald.cron@arm.com> Reviewed-By: Olivier Martin <olivier.martin@arm.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@15833 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			257 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			257 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
 | 
						|
  Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>
 | 
						|
  Copyright (c) 2011 - 2014, ARM Limited. All rights reserved.
 | 
						|
 | 
						|
  This program and the accompanying materials
 | 
						|
  are licensed and made available under the terms and conditions of the BSD License
 | 
						|
  which accompanies this distribution.  The full text of the license may be found at
 | 
						|
  http://opensource.org/licenses/bsd-license.php
 | 
						|
 | 
						|
  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | 
						|
  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | 
						|
 | 
						|
**/
 | 
						|
 | 
						|
#include <Base.h>
 | 
						|
 | 
						|
#include <Library/BaseLib.h>
 | 
						|
#include <Library/TimerLib.h>
 | 
						|
#include <Library/DebugLib.h>
 | 
						|
#include <Library/PcdLib.h>
 | 
						|
#include <Library/IoLib.h>
 | 
						|
#include <Drivers/SP804Timer.h>
 | 
						|
 | 
						|
#define SP804_TIMER_METRONOME_BASE    ((UINTN)PcdGet32 (PcdSP804TimerMetronomeBase))
 | 
						|
#define SP804_TIMER_PERFORMANCE_BASE  ((UINTN)PcdGet32 (PcdSP804TimerPerformanceBase))
 | 
						|
 | 
						|
// Setup SP810's Timer2 for managing delay functions. And Timer3 for Performance counter
 | 
						|
// Note: ArmVE's Timer0 and Timer1 are used by TimerDxe.
 | 
						|
RETURN_STATUS
 | 
						|
EFIAPI
 | 
						|
TimerConstructor (
 | 
						|
  VOID
 | 
						|
  )
 | 
						|
{
 | 
						|
  // Check if the Metronome Timer is already initialized
 | 
						|
  if ((MmioRead32 (SP804_TIMER_METRONOME_BASE + SP804_TIMER_CONTROL_REG) & SP804_TIMER_CTRL_ENABLE) == 0) {
 | 
						|
    // Configure the Metronome Timer for free running operation, 32 bits, no prescaler, and interrupt disabled
 | 
						|
    MmioWrite32 (SP804_TIMER_METRONOME_BASE + SP804_TIMER_CONTROL_REG, SP804_TIMER_CTRL_32BIT | SP804_PRESCALE_DIV_1);
 | 
						|
 | 
						|
    // Start the Metronome Timer ticking
 | 
						|
    MmioOr32 (SP804_TIMER_METRONOME_BASE + SP804_TIMER_CONTROL_REG, SP804_TIMER_CTRL_ENABLE);
 | 
						|
  }
 | 
						|
 | 
						|
  // Check if the Performance Timer is already initialized
 | 
						|
  if ((MmioRead32 (SP804_TIMER_PERFORMANCE_BASE + SP804_TIMER_CONTROL_REG) & SP804_TIMER_CTRL_ENABLE) == 0) {
 | 
						|
    // Configure the Performance timer for free running operation, 32 bits, no prescaler, interrupt disabled
 | 
						|
    MmioWrite32 (SP804_TIMER_PERFORMANCE_BASE + SP804_TIMER_CONTROL_REG, SP804_TIMER_CTRL_32BIT | SP804_PRESCALE_DIV_1);
 | 
						|
 | 
						|
    // Start the Performance Timer ticking
 | 
						|
    MmioOr32 (SP804_TIMER_PERFORMANCE_BASE + SP804_TIMER_CONTROL_REG, SP804_TIMER_CTRL_ENABLE);
 | 
						|
  }
 | 
						|
 | 
						|
  return RETURN_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Stalls the CPU for at least the given number of microseconds.
 | 
						|
 | 
						|
  Stalls the CPU for the number of microseconds specified by MicroSeconds.
 | 
						|
  The hardware timer is 32 bits.
 | 
						|
  The maximum possible delay is (0xFFFFFFFF / TimerFrequencyMHz), i.e. ([32bits] / FreqInMHz)
 | 
						|
  For example:
 | 
						|
  +----------------+------------+----------+----------+
 | 
						|
  | TimerFrequency |  MaxDelay  | MaxDelay | MaxDelay |
 | 
						|
  |     (MHz)      |    (us)    |   (s)    |  (min)   |
 | 
						|
  +----------------+------------+----------+----------+
 | 
						|
  |        1       | 0xFFFFFFFF |   4294   |   71.5   |
 | 
						|
  |        5       | 0x33333333 |    859   |   14.3   |
 | 
						|
  |       10       | 0x19999999 |    429   |    7.2   |
 | 
						|
  |       50       | 0x051EB851 |     86   |    1.4   |
 | 
						|
  +----------------+------------+----------+----------+
 | 
						|
  If it becomes necessary to support higher delays, then consider using the
 | 
						|
  real time clock.
 | 
						|
 | 
						|
  During this delay, the cpu is not yielded to any other process, with one exception:
 | 
						|
  events that are triggered off a timer and which execute at a higher TPL than
 | 
						|
  this function. These events may call MicroSecondDelay (or NanoSecondDelay) to
 | 
						|
  fulfil their own needs.
 | 
						|
  Therefore, this function must be re-entrant, as it may be interrupted and re-started.
 | 
						|
 | 
						|
  @param  MicroSeconds  The minimum number of microseconds to delay.
 | 
						|
 | 
						|
  @return The value of MicroSeconds inputted.
 | 
						|
 | 
						|
**/
 | 
						|
UINTN
 | 
						|
EFIAPI
 | 
						|
MicroSecondDelay (
 | 
						|
  IN  UINTN MicroSeconds
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT64    DelayTicks64;         // Convert from microseconds to timer ticks, more bits to detect over-range conditions.
 | 
						|
  UINTN     DelayTicks;           // Convert from microseconds to timer ticks, native size for general calculations.
 | 
						|
  UINTN     StartTicks;           // Timer value snapshot at the start of the delay
 | 
						|
  UINTN     TargetTicks;          // Timer value to signal the end of the delay
 | 
						|
  UINTN     CurrentTicks;         // Current value of the 64-bit timer value at any given moment
 | 
						|
 | 
						|
  // If we snapshot the timer at the start of the delay function then we minimise unaccounted overheads.
 | 
						|
  StartTicks = MmioRead32 (SP804_TIMER_METRONOME_BASE + SP804_TIMER_CURRENT_REG);
 | 
						|
 | 
						|
  // We are operating at the limit of 32bits. For the range checking work in 64 bits to avoid overflows.
 | 
						|
  DelayTicks64 = MultU64x32((UINT64)MicroSeconds, PcdGet32(PcdSP804TimerFrequencyInMHz));
 | 
						|
 | 
						|
  // We are limited to 32 bits.
 | 
						|
  // If the specified delay is exactly equal to the max range of the timer,
 | 
						|
  // then the start will be equal to the stop plus one timer overflow (wrap-around).
 | 
						|
  // To avoid having to check for that, reduce the maximum acceptable range by 1 tick,
 | 
						|
  // i.e. reject delays equal or greater than the max range of the timer.
 | 
						|
  if (DelayTicks64 >= (UINT64)SP804_MAX_TICKS) {
 | 
						|
    DEBUG((EFI_D_ERROR,"MicroSecondDelay: ERROR: MicroSeconds=%d exceed SP804 count range. Max MicroSeconds=%d\n",
 | 
						|
      MicroSeconds,
 | 
						|
      ((UINTN)SP804_MAX_TICKS/PcdGet32(PcdSP804TimerFrequencyInMHz))));
 | 
						|
  }
 | 
						|
  ASSERT(DelayTicks64 < (UINT64)SP804_MAX_TICKS);
 | 
						|
 | 
						|
  // From now on do calculations only in native bit size.
 | 
						|
  DelayTicks = (UINTN)DelayTicks64;
 | 
						|
 | 
						|
  // Calculate the target value of the timer.
 | 
						|
 | 
						|
  //Note: SP804 timer is counting down
 | 
						|
  if (StartTicks >= DelayTicks) {
 | 
						|
    // In this case we do not expect a wrap-around of the timer to occur.
 | 
						|
    // CurrentTicks must be less than StartTicks and higher than TargetTicks.
 | 
						|
    // If this is not the case, then the delay has been reached and may even have been exceeded if this
 | 
						|
    // function was suspended by a higher priority interrupt.
 | 
						|
 | 
						|
    TargetTicks = StartTicks - DelayTicks;
 | 
						|
 | 
						|
    do {
 | 
						|
      CurrentTicks = MmioRead32 (SP804_TIMER_METRONOME_BASE + SP804_TIMER_CURRENT_REG);
 | 
						|
    } while ((CurrentTicks > TargetTicks) && (CurrentTicks <= StartTicks));
 | 
						|
 | 
						|
  } else {
 | 
						|
    // In this case TargetTicks is larger than StartTicks.
 | 
						|
    // This means we expect a wrap-around of the timer to occur and we must wait for it.
 | 
						|
    // Before the wrap-around, CurrentTicks must be less than StartTicks and less than TargetTicks.
 | 
						|
    // After the wrap-around, CurrentTicks must be larger than StartTicks and larger than TargetTicks.
 | 
						|
    // If this is not the case, then the delay has been reached and may even have been exceeded if this
 | 
						|
    // function was suspended by a higher priority interrupt.
 | 
						|
 | 
						|
    // The order of operations is essential to avoid arithmetic overflow problems
 | 
						|
    TargetTicks = ((UINTN)SP804_MAX_TICKS - DelayTicks) + StartTicks;
 | 
						|
 | 
						|
    // First wait for the wrap-around to occur
 | 
						|
    do {
 | 
						|
      CurrentTicks = MmioRead32 (SP804_TIMER_METRONOME_BASE + SP804_TIMER_CURRENT_REG);
 | 
						|
    } while (CurrentTicks <= StartTicks);
 | 
						|
 | 
						|
    // Then wait for the target
 | 
						|
    do {
 | 
						|
      CurrentTicks = MmioRead32 (SP804_TIMER_METRONOME_BASE + SP804_TIMER_CURRENT_REG);
 | 
						|
    } while (CurrentTicks > TargetTicks);
 | 
						|
  }
 | 
						|
 | 
						|
  return MicroSeconds;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Stalls the CPU for at least the given number of nanoseconds.
 | 
						|
 | 
						|
  Stalls the CPU for the number of nanoseconds specified by NanoSeconds.
 | 
						|
 | 
						|
  When the timer frequency is 1MHz, each tick corresponds to 1 microsecond.
 | 
						|
  Therefore, the nanosecond delay will be rounded up to the nearest 1 microsecond.
 | 
						|
 | 
						|
  @param  NanoSeconds The minimum number of nanoseconds to delay.
 | 
						|
 | 
						|
  @return The value of NanoSeconds inputted.
 | 
						|
 | 
						|
**/
 | 
						|
UINTN
 | 
						|
EFIAPI
 | 
						|
NanoSecondDelay (
 | 
						|
  IN  UINTN NanoSeconds
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN  MicroSeconds;
 | 
						|
 | 
						|
  // Round up to 1us Tick Number
 | 
						|
  MicroSeconds = NanoSeconds / 1000;
 | 
						|
  MicroSeconds += ((NanoSeconds % 1000) == 0) ? 0 : 1;
 | 
						|
 | 
						|
  MicroSecondDelay (MicroSeconds);
 | 
						|
 | 
						|
  return NanoSeconds;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Retrieves the current value of a 64-bit free running performance counter.
 | 
						|
 | 
						|
  The counter can either count up by 1 or count down by 1. If the physical
 | 
						|
  performance counter counts by a larger increment, then the counter values
 | 
						|
  must be translated. The properties of the counter can be retrieved from
 | 
						|
  GetPerformanceCounterProperties().
 | 
						|
 | 
						|
  @return The current value of the free running performance counter.
 | 
						|
 | 
						|
**/
 | 
						|
UINT64
 | 
						|
EFIAPI
 | 
						|
GetPerformanceCounter (
 | 
						|
  VOID
 | 
						|
  )
 | 
						|
{
 | 
						|
  // Free running 64-bit/32-bit counter is needed here.
 | 
						|
  // Don't think we need this to boot, just to do performance profile
 | 
						|
  UINT64 Value;
 | 
						|
  Value = MmioRead32 (SP804_TIMER_PERFORMANCE_BASE + SP804_TIMER_CURRENT_REG);
 | 
						|
  return Value;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Retrieves the 64-bit frequency in Hz and the range of performance counter
 | 
						|
  values.
 | 
						|
 | 
						|
  If StartValue is not NULL, then the value that the performance counter starts
 | 
						|
  with immediately after is it rolls over is returned in StartValue. If
 | 
						|
  EndValue is not NULL, then the value that the performance counter end with
 | 
						|
  immediately before it rolls over is returned in EndValue. The 64-bit
 | 
						|
  frequency of the performance counter in Hz is always returned. If StartValue
 | 
						|
  is less than EndValue, then the performance counter counts up. If StartValue
 | 
						|
  is greater than EndValue, then the performance counter counts down. For
 | 
						|
  example, a 64-bit free running counter that counts up would have a StartValue
 | 
						|
  of 0 and an EndValue of 0xFFFFFFFFFFFFFFFF. A 24-bit free running counter
 | 
						|
  that counts down would have a StartValue of 0xFFFFFF and an EndValue of 0.
 | 
						|
 | 
						|
  @param  StartValue  The value the performance counter starts with when it
 | 
						|
                      rolls over.
 | 
						|
  @param  EndValue    The value that the performance counter ends with before
 | 
						|
                      it rolls over.
 | 
						|
 | 
						|
  @return The frequency in Hz.
 | 
						|
 | 
						|
**/
 | 
						|
UINT64
 | 
						|
EFIAPI
 | 
						|
GetPerformanceCounterProperties (
 | 
						|
  OUT UINT64  *StartValue,  OPTIONAL
 | 
						|
  OUT UINT64  *EndValue     OPTIONAL
 | 
						|
  )
 | 
						|
{
 | 
						|
  if (StartValue != NULL) {
 | 
						|
    // Timer starts with the reload value
 | 
						|
    *StartValue = 0xFFFFFFFF;
 | 
						|
  }
 | 
						|
 | 
						|
  if (EndValue != NULL) {
 | 
						|
    // Timer counts down to 0x0
 | 
						|
    *EndValue = (UINT64)0ULL;
 | 
						|
  }
 | 
						|
 | 
						|
  return PcdGet64 (PcdEmbeddedPerformanceCounterFrequencyInHz);
 | 
						|
}
 |