summaryrefslogtreecommitdiff
path: root/PerformancePkg/Library/TscTimerLib/TscTimerLib.c
diff options
context:
space:
mode:
Diffstat (limited to 'PerformancePkg/Library/TscTimerLib/TscTimerLib.c')
-rw-r--r--PerformancePkg/Library/TscTimerLib/TscTimerLib.c243
1 files changed, 243 insertions, 0 deletions
diff --git a/PerformancePkg/Library/TscTimerLib/TscTimerLib.c b/PerformancePkg/Library/TscTimerLib/TscTimerLib.c
new file mode 100644
index 0000000000..19151a4e9b
--- /dev/null
+++ b/PerformancePkg/Library/TscTimerLib/TscTimerLib.c
@@ -0,0 +1,243 @@
+/** @file
+ A Timer Library implementation which uses the Time Stamp Counter in the processor.
+
+ For Pentium 4 processors, Intel Xeon processors (family [0FH], models [03H and higher]);
+ for Intel Core Solo and Intel Core Duo processors (family [06H], model [0EH]);
+ for the Intel Xeon processor 5100 series and Intel Core 2 Duo processors (family [06H], model [0FH]);
+ for Intel Core 2 and Intel Xeon processors (family [06H], display_model [17H]);
+ for Intel Atom processors (family [06H], display_model [1CH]):
+ the time-stamp counter increments at a constant rate.
+ That rate may be set by the maximum core-clock to bus-clock ratio of the processor or may be set by
+ the maximum resolved frequency at which the processor is booted. The maximum resolved frequency may
+ differ from the maximum qualified frequency of the processor.
+
+ The specific processor configuration determines the behavior. Constant TSC behavior ensures that the
+ duration of each clock tick is uniform and supports the use of the TSC as a wall clock timer even if
+ the processor core changes frequency. This is the architectural behavior moving forward.
+
+ A Processor’s support for invariant TSC is indicated by CPUID.0x80000007.EDX[8].
+
+ Copyright (c) 2009-2010, Intel Corporation. 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 <Ich/GenericIch.h>
+
+#include <Library/TimerLib.h>
+#include <Library/BaseLib.h>
+#include <Library/IoLib.h>
+#include <Library/PciLib.h>
+#include <Library/PcdLib.h>
+
+STATIC UINT64 mTscFrequency;
+
+#ifndef R_ICH_ACPI_PM1_TMR
+#define R_ICH_ACPI_PM1_TMR R_ACPI_PM1_TMR
+#endif
+
+/** The constructor function determines the actual TSC frequency.
+
+ The TSC counting frequency is determined by comparing how far it counts
+ during a 1ms period as determined by the ACPI timer. The ACPI timer is
+ used because it counts at a known frequency.
+ If ACPI I/O space not enabled, this function will enable it. Then the
+ TSC is sampled, followed by waiting for 3579 clocks of the ACPI timer, or 1ms.
+ The TSC is then sampled again. The difference multiplied by 1000 is the TSC
+ frequency. There will be a small error because of the overhead of reading
+ the ACPI timer. An attempt is made to determine and compensate for this error.
+ This function will always return RETURN_SUCCESS.
+
+ @retval RETURN_SUCCESS The constructor always returns RETURN_SUCCESS.
+
+**/
+RETURN_STATUS
+EFIAPI
+TscTimerLibConstructor (
+ VOID
+ )
+{
+ UINT64 StartTSC;
+ UINT64 EndTSC;
+ UINT32 TimerAddr;
+ UINT32 Ticks;
+
+ //
+ // If ACPI I/O space is not enabled yet, program ACPI I/O base address and enable it.
+ //
+ if ((PciRead8 (PCI_ICH_LPC_ADDRESS (R_ICH_LPC_ACPI_CNT)) & B_ICH_LPC_ACPI_CNT_ACPI_EN) == 0) {
+ PciWrite16 (PCI_ICH_LPC_ADDRESS (R_ICH_LPC_ACPI_BASE), PcdGet16 (PcdPerfPkgAcpiIoPortBaseAddress));
+ PciOr8 (PCI_ICH_LPC_ADDRESS (R_ICH_LPC_ACPI_CNT), B_ICH_LPC_ACPI_CNT_ACPI_EN);
+ }
+
+ TimerAddr = PcdGet16 (PcdPerfPkgAcpiIoPortBaseAddress) + R_ACPI_PM1_TMR; // Locate the ACPI Timer
+ Ticks = IoRead32( TimerAddr) + (3579); // Set Ticks to 1ms in the future
+ StartTSC = AsmReadTsc(); // Get base value for the TSC
+ //
+ // Wait until the ACPI timer has counted 1ms.
+ // Timer wrap-arounds are handled correctly by this function.
+ // When the current ACPI timer value is greater than 'Ticks', the while loop will exit.
+ //
+ while (((Ticks - IoRead32( TimerAddr)) & BIT23) == 0) {
+ CpuPause();
+ }
+ EndTSC = AsmReadTsc(); // TSC value 1ms later
+
+ mTscFrequency = MultU64x32 (
+ (EndTSC - StartTSC), // Number of TSC counts in 1ms
+ 1000 // Number of ms in a second
+ );
+ //
+ // mTscFrequency is now equal to the number of TSC counts per second
+ //
+ return RETURN_SUCCESS;
+}
+
+/** Stalls the CPU for at least the given number of ticks.
+
+ Stalls the CPU for at least the given number of ticks. It's invoked by
+ MicroSecondDelay() and NanoSecondDelay().
+
+ @param[in] Delay A period of time to delay in ticks.
+
+**/
+STATIC
+VOID
+InternalX86Delay (
+ IN UINT64 Delay
+ )
+{
+ UINT64 Ticks;
+
+ //
+ // The target timer count is calculated here
+ //
+ Ticks = AsmReadTsc() + Delay;
+
+ //
+ // Wait until time out
+ // Timer wrap-arounds are NOT handled correctly by this function.
+ // Thus, this function must be called within 10 years of reset since
+ // Intel guarantees a minimum of 10 years before the TSC wraps.
+ //
+ while (AsmReadTsc() <= Ticks) CpuPause();
+}
+
+/** Stalls the CPU for at least the specified number of MicroSeconds.
+
+ @param[in] MicroSeconds The minimum number of microseconds to delay.
+
+ @return The value of MicroSeconds input.
+
+**/
+UINTN
+EFIAPI
+MicroSecondDelay (
+ IN UINTN MicroSeconds
+ )
+{
+ InternalX86Delay (
+ DivU64x32 (
+ MultU64x64 (
+ mTscFrequency,
+ MicroSeconds
+ ),
+ 1000000u
+ )
+ );
+ return MicroSeconds;
+}
+
+/** Stalls the CPU for at least the specified number of NanoSeconds.
+
+ @param[in] NanoSeconds The minimum number of nanoseconds to delay.
+
+ @return The value of NanoSeconds input.
+
+**/
+UINTN
+EFIAPI
+NanoSecondDelay (
+ IN UINTN NanoSeconds
+ )
+{
+ InternalX86Delay (
+ DivU64x32 (
+ MultU64x32 (
+ mTscFrequency,
+ (UINT32)NanoSeconds
+ ),
+ 1000000000u
+ )
+ );
+ return NanoSeconds;
+}
+
+/** Retrieves the current value of the 64-bit free running Time-Stamp counter.
+
+ The time-stamp counter (as implemented in the P6 family, Pentium, Pentium M,
+ Pentium 4, Intel Xeon, Intel Core Solo and Intel Core Duo processors and
+ later processors) is a 64-bit counter that is set to 0 following a RESET of
+ the processor. Following a RESET, the counter increments even when the
+ processor is halted by the HLT instruction or the external STPCLK# pin. Note
+ that the assertion of the external DPSLP# pin may cause the time-stamp
+ counter to stop.
+
+ The properties of the counter can be retrieved by the
+ GetPerformanceCounterProperties() function.
+
+ @return The current value of the free running performance counter.
+
+**/
+UINT64
+EFIAPI
+GetPerformanceCounter (
+ VOID
+ )
+{
+ return AsmReadTsc();
+}
+
+/** 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, 0x0, is returned in StartValue. If EndValue is not NULL, then the value
+ that the performance counter end with, 0xFFFFFFFFFFFFFFFF, is returned in
+ EndValue.
+
+ The 64-bit frequency of the performance counter, in Hz, is always returned.
+ To determine average processor clock frequency, Intel recommends the use of
+ EMON logic to count processor core clocks over the period of time for which
+ the average is required.
+
+
+ @param[out] StartValue Pointer to where the performance counter's starting value is saved, or NULL.
+ @param[out] EndValue Pointer to where the performance counter's ending value is saved, or NULL.
+
+ @return The frequency in Hz.
+
+**/
+UINT64
+EFIAPI
+GetPerformanceCounterProperties (
+ OUT UINT64 *StartValue, OPTIONAL
+ OUT UINT64 *EndValue OPTIONAL
+ )
+{
+ if (StartValue != NULL) {
+ *StartValue = 0;
+ }
+ if (EndValue != NULL) {
+ *EndValue = 0xFFFFFFFFFFFFFFFFull;
+ }
+
+ return mTscFrequency;
+}