summaryrefslogtreecommitdiffstats
path: root/PerformancePkg/Library/TscTimerLib/TscTimerLibShare.c
diff options
context:
space:
mode:
Diffstat (limited to 'PerformancePkg/Library/TscTimerLib/TscTimerLibShare.c')
-rw-r--r--PerformancePkg/Library/TscTimerLib/TscTimerLibShare.c275
1 files changed, 275 insertions, 0 deletions
diff --git a/PerformancePkg/Library/TscTimerLib/TscTimerLibShare.c b/PerformancePkg/Library/TscTimerLib/TscTimerLibShare.c
new file mode 100644
index 0000000000..161af00237
--- /dev/null
+++ b/PerformancePkg/Library/TscTimerLib/TscTimerLibShare.c
@@ -0,0 +1,275 @@
+/** @file
+ The 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 - 2011, 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 "TscTimerLibInternal.h"
+
+/** Calculate 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.
+
+ @return The number of TSC counts per second.
+
+**/
+UINT64
+InternalCalculateTscFrequency (
+ VOID
+ )
+{
+ UINT64 StartTSC;
+ UINT64 EndTSC;
+ UINT32 TimerAddr;
+ UINT32 Ticks;
+ UINT64 TscFrequency;
+
+ //
+ // 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);
+ }
+
+ //
+ // ACPI I/O space should be enabled now, locate the ACPI Timer.
+ // ACPI I/O base address maybe have be initialized by other driver with different value,
+ // So get it from PCI space directly.
+ //
+ TimerAddr = ((PciRead16 (PCI_ICH_LPC_ADDRESS (R_ICH_LPC_ACPI_BASE))) & B_ICH_LPC_ACPI_BASE_BAR) + R_ACPI_PM1_TMR;
+ 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
+
+ TscFrequency = MultU64x32 (
+ (EndTSC - StartTSC), // Number of TSC counts in 1ms
+ 1000 // Number of ms in a second
+ );
+
+ return TscFrequency;
+}
+
+/** 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.
+
+**/
+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 (
+ InternalGetTscFrequency (),
+ 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 (
+ InternalGetTscFrequency (),
+ (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 InternalGetTscFrequency ();
+}
+
+/**
+ Converts elapsed ticks of performance counter to time in nanoseconds.
+
+ This function converts the elapsed ticks of running performance counter to
+ time value in unit of nanoseconds.
+
+ @param Ticks The number of elapsed ticks of running performance counter.
+
+ @return The elapsed time in nanoseconds.
+
+**/
+UINT64
+EFIAPI
+GetTimeInNanoSecond (
+ IN UINT64 Ticks
+ )
+{
+ UINT64 Frequency;
+ UINT64 NanoSeconds;
+ UINT64 Remainder;
+ INTN Shift;
+
+ Frequency = GetPerformanceCounterProperties (NULL, NULL);
+
+ //
+ // Ticks
+ // Time = --------- x 1,000,000,000
+ // Frequency
+ //
+ NanoSeconds = MultU64x32 (DivU64x64Remainder (Ticks, Frequency, &Remainder), 1000000000u);
+
+ //
+ // Ensure (Remainder * 1,000,000,000) will not overflow 64-bit.
+ // Since 2^29 < 1,000,000,000 = 0x3B9ACA00 < 2^30, Remainder should < 2^(64-30) = 2^34,
+ // i.e. highest bit set in Remainder should <= 33.
+ //
+ Shift = MAX (0, HighBitSet64 (Remainder) - 33);
+ Remainder = RShiftU64 (Remainder, (UINTN) Shift);
+ Frequency = RShiftU64 (Frequency, (UINTN) Shift);
+ NanoSeconds += DivU64x64Remainder (MultU64x32 (Remainder, 1000000000u), Frequency, NULL);
+
+ return NanoSeconds;
+}