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