Add accurate TSC frequency calculation support

Current SBL code uses MSR(0xce) to calculate the CPU TSC frequency.
However, it is not very accurate. A better way is to use CPUID to
calculate the TSC frequency. This patch added new API to get accurate
TSC frequency.  It also added APIs to allow conversion between time
and TSC ticks.

Signed-off-by: Maurice Ma <maurice.ma@intel.com>
This commit is contained in:
Maurice Ma 2021-10-02 19:25:55 -07:00
parent 4d45a48ac0
commit 8c85533285
4 changed files with 132 additions and 5 deletions

View File

@ -1,6 +1,6 @@
/** @file
Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
Copyright (c) 2017 - 2021, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
@ -32,4 +32,47 @@ GetTimeStampFrequency (
VOID
);
/**
Get timestamp accurate frequency in HZ by CPUID.
The TSC counting frequency is determined by using CPUID leaf 0x15. Frequency in MHz = Core XTAL frequency * EBX/EAX.
In newer flavors of the CPU, core xtal frequency is returned in ECX or 0 if not supported.
@retval The number of TSC counts per second.
**/
UINT64
EFIAPI
GetTimeStampAccurateFrequency (
VOID
);
/**
Convert timestamp ticks to microseconds.
@param[in] Ticks The number of timestamp ticks to convert.
@retval MicroSeconds
**/
UINT64
EFIAPI
TimeStampTickToMicroSecond (
IN UINT64 Ticks
);
/**
Convert microseconds to timestamp ticks.
@param[in] MicroSeconds The number of microseconds to convert.
@retval Timestamp ticks
**/
UINT64
EFIAPI
MicroSecondToTimeStampTick (
IN UINT64 MicroSeconds
);
#endif

View File

@ -1,10 +1,11 @@
/** @file
Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
Copyright (c) 2017 - 2021, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Register/Intel/Cpuid.h>
#include <Library/BaseLib.h>
/**
@ -44,3 +45,85 @@ GetTimeStampFrequency (
// Ratio * 100000
return (UINT32)(Ratio * 100000);
}
/**
Get timestamp accurate frequency in HZ by CPUID.
The TSC counting frequency is determined by using CPUID leaf 0x15. Frequency in MHz = Core XTAL frequency * EBX/EAX.
In newer flavors of the CPU, core xtal frequency is returned in ECX or 0 if not supported.
@retval The number of TSC counts per second.
**/
UINT64
EFIAPI
GetTimeStampAccurateFrequency (
VOID
)
{
UINT64 TscFrequency;
UINT32 RegEax;
UINT32 RegEbx;
UINT32 RegEcx;
// Use CPUID leaf 0x15 Time Stamp Counter and Nominal Core Crystal Clock Information
// EBX returns 0 if not supported. ECX, if non zero, provides Core Xtal Frequency in hertz.
// TSC frequency = (ECX, Core Xtal Frequency) * EBX/EAX.
AsmCpuid (CPUID_TIME_STAMP_COUNTER, &RegEax, &RegEbx, &RegEcx, NULL);
// If EAX, EBX or ECX returns 0, the XTAL ratio is not enumerated.
if ((RegEax == 0) || (RegEbx == 0 ) || (RegEcx == 0)) {
// Fallback to use GetTimeStampFrequency() instead
TscFrequency = MultU64x32 (GetTimeStampFrequency(), 1000);
} else {
// Calculate TSC frequency = (ECX, Core Xtal Frequency) * EBX/EAX
TscFrequency = DivU64x32 (MultU64x32 (RegEcx, RegEbx) + (UINT64)(RegEax >> 1), RegEax);
}
return TscFrequency;
}
/**
Convert microseconds to timestamp ticks.
@param[in] MicroSeconds The number of microseconds to convert.
@retval Timestamp ticks
**/
UINT64
EFIAPI
MicroSecondToTimeStampTick (
IN UINT64 MicroSeconds
)
{
return DivU64x32 (
MultU64x64 (
GetTimeStampAccurateFrequency (),
MicroSeconds
),
1000000u
);
}
/**
Convert timestamp ticks to microseconds.
@param[in] Ticks The number of timestamp ticks to convert.
@retval MicroSeconds
**/
UINT64
EFIAPI
TimeStampTickToMicroSecond (
IN UINT64 Ticks
)
{
return DivU64x64Remainder (
MultU64x32 (Ticks, 1000000u),
GetTimeStampAccurateFrequency (),
NULL
);
}

View File

@ -1,6 +1,6 @@
## @file
#
# Copyright (c) 2017 - 2020, Intel Corporation. All rights reserved.<BR>
# Copyright (c) 2017 - 2021, Intel Corporation. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
@ -24,6 +24,7 @@
[Packages]
MdePkg/MdePkg.dec
BootloaderCommonPkg/BootloaderCommonPkg.dec
[Pcd]

View File

@ -48,7 +48,7 @@ GpioDebugPortWriteByte (
UINT64 Ts1;
Baud = PcdGet32 (PcdGpioDebugPortBaudRate);
Freq = GetTimeStampFrequency ();
Freq = GetTimeStampAccurateFrequency ();
// Prepare 10 bits, 1 start bit, 8 data bits, 1 stop bits
Word = (Value << 1) | BIT9;
@ -56,7 +56,7 @@ GpioDebugPortWriteByte (
for (Idx = 0; Idx < 10; Idx++) {
SetGpioTxPin (Word & (1 << Idx));
// Wait for 1 bit calculated by current baud rate
Ts1 = Ts0 + DivU64x32 (MultU64x32(Freq, (Idx + 1) * 1000), Baud);
Ts1 = Ts0 + DivU64x32 (MultU64x32(Freq, Idx + 1), Baud);
while (ReadTimeStamp () < Ts1) {
CpuPause ();
}