Old logic is simply to get the APIC Timer Frequency from PcdFSBClock.

New logic follows the SDM to firstly get the crystal clock frequency from
CPUID.0x15 leaf. If CPUID.0x15 is not supported, the crystal clock frequency
is retrieved from PcdCpuCoreCrystalClockFrequency.
Then the new logic finds a proper divisor that could support longer timer
period. Usually divisor 1 is good enough. But when the timer period is very
long, divisor 4, 8, or even 128 could be used.

Signed-off-by: Ray Ni <ray...@intel.com>
Cc: Michael D Kinney <michael.d.kin...@intel.com>
Cc: Nate DeSimone <nathaniel.l.desim...@intel.com>
Cc: Laszlo Ersek <ler...@redhat.com>
Cc: Rahul Kumar <rahul1.ku...@intel.com>
Cc: Gerd Hoffmann <kra...@redhat.com>
---
 .../LocalApicTimerDxe/LocalApicTimerDxe.c     | 120 +++++++++++++++---
 .../LocalApicTimerDxe/LocalApicTimerDxe.h     |   4 +-
 .../LocalApicTimerDxe/LocalApicTimerDxe.inf   |   2 +-
 3 files changed, 107 insertions(+), 19 deletions(-)

diff --git a/UefiCpuPkg/LocalApicTimerDxe/LocalApicTimerDxe.c 
b/UefiCpuPkg/LocalApicTimerDxe/LocalApicTimerDxe.c
index f36a0e6bf3..babf2476e3 100644
--- a/UefiCpuPkg/LocalApicTimerDxe/LocalApicTimerDxe.c
+++ b/UefiCpuPkg/LocalApicTimerDxe/LocalApicTimerDxe.c
@@ -128,6 +128,50 @@ TimerDriverRegisterHandler (
   return EFI_SUCCESS;
 }
 
+/**
+  Return the Crystal Clock Frequency in Hz.
+
+  If CPUID.0x15 is supported, the Crystal Clock Frequency is retrieved from 
CPUID.0x15.ECX.
+  Otherwise, the Crystal Clock Frequency is retrieved from 
PcdCpuCoreCrystalClockFrequency.
+
+  @return the Crystal Clock Frequency in Hz.
+**/
+UINT64
+GetCoreXtalClockFrequency (
+  VOID
+  )
+{
+  UINT32  MaxLeaf;
+  UINT32  RegEax;
+  UINT32  RegEbx;
+  UINT32  CoreXtalClockFrequency;
+
+  CoreXtalClockFrequency = 0;
+  AsmCpuid (CPUID_SIGNATURE, &MaxLeaf, NULL, NULL, NULL);
+  if (MaxLeaf >= CPUID_TIME_STAMP_COUNTER) {
+    //
+    // 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, (UINT32 
*)&CoreXtalClockFrequency, NULL);
+
+    //
+    // If EAX or EBX returns 0, the Crystal ratio is not enumerated.
+    //
+    if ((RegEax == 0) || (RegEbx == 0)) {
+      CoreXtalClockFrequency = 0;
+    }
+  }
+
+  if (CoreXtalClockFrequency != 0) {
+    return CoreXtalClockFrequency;
+  } else {
+    DEBUG ((DEBUG_INFO, "%a: Using PcdCpuCoreCrystalClockFrequency (%ld)\n", 
__func__, PcdGet64 (PcdCpuCoreCrystalClockFrequency)));
+    return PcdGet64 (PcdCpuCoreCrystalClockFrequency);
+  }
+}
+
 /**
 
   This function adjusts the period of timer interrupts to the value specified
@@ -163,9 +207,13 @@ TimerDriverSetTimerPeriod (
   IN UINT64                   TimerPeriod
   )
 {
-  UINT64  TimerCount;
-  UINT32  TimerFrequency;
-  UINT32  DivideValue = 1;
+  UINT64  InitialCount;
+  UINT64  CoreXtalClockFrequency;
+  UINT64  ApicTimerFrequency;
+  UINT8   Divisor;
+  UINT32  TimerPeriodDivisor;
+
+  DEBUG ((DEBUG_INFO, "%a: TimerPeriod = %d (100ns)\n", __func__, 
TimerPeriod));
 
   if (TimerPeriod == 0) {
     //
@@ -173,29 +221,67 @@ TimerDriverSetTimerPeriod (
     //
     DisableApicTimerInterrupt ();
   } else {
-    TimerFrequency = PcdGet32 (PcdFSBClock) / (UINT32)DivideValue;
+    CoreXtalClockFrequency = GetCoreXtalClockFrequency ();
+    //
+    // Find a good Divisor that can support the TimerPeriod.
+    //
+    for (Divisor = 1; Divisor <= 128; Divisor *= 2) {
+      ApicTimerFrequency = DivU64x32 (CoreXtalClockFrequency, Divisor);
+
+      //
+      //                   TimerPeriod
+      // InitialCount =   ---------------- * ApicTimerFrequency
+      //                  10 * 1000 * 1000
+      //
+      // So,
+      //
+      //                        InitialCount * 10 * 1000 * 1000
+      // ApicTimerFrequency =   ------------------------------
+      //                                TimerPeriod
+      //
+      // Because InitialCount is a UINT32, the maximum ApicTimerFrequency is:
+      //
+      //                        MAX_UINT32 * 10 * 1000 * 1000
+      //                        ------------------------------
+      //                                TimerPeriod
+      //
+      if (ApicTimerFrequency <= DivU64x64Remainder (MultU64x32 (MAX_UINT32, 10 
* 1000 * 1000), TimerPeriod, NULL)) {
+        break;
+      }
+    }
+
+    DEBUG ((
+      DEBUG_INFO,
+      "%a: ApicTimerFrequency (%d) = CoreXtalClockFrequency (%d) / Divisor 
(%d)\n",
+      __func__,
+      ApicTimerFrequency,
+      CoreXtalClockFrequency,
+      Divisor
+      ));
 
     //
-    // Convert TimerPeriod into local APIC counts
+    // Convert TimerPeriod (in 100ns) into local APIC counts
+    //                   TimerPeriod
+    // InitialCount =   ---------------- * ApicTimerFrequency
+    //                10 * 1000 * 1000
     //
-    // TimerPeriod is in 100ns
-    // TimerPeriod/10000000 will be in seconds.
-    TimerCount = DivU64x32 (
-                   MultU64x32 (TimerPeriod, TimerFrequency),
-                   10000000
-                   );
+    TimerPeriodDivisor = 10 * 1000 * 1000;
 
-    // Check for overflow
-    if (TimerCount > MAX_UINT32) {
-      TimerCount = MAX_UINT32;
-      /* TimerPeriod = (MAX_UINT32 / TimerFrequency) * 10000000; */
-      TimerPeriod = 429496730;
+    //
+    // If TimerPeriod * ApicTimerFrequency > MAX_UINT64, divide TimerPeriod by 
10 until the result <= MAX_UINT64.
+    //
+    while (TimerPeriod > DivU64x64Remainder (MAX_UINT64, ApicTimerFrequency, 
NULL)) {
+      TimerPeriod         = DivU64x32 (TimerPeriod, 10);
+      TimerPeriodDivisor /= 10;
     }
 
+    InitialCount = DivU64x64Remainder (MultU64x64 (TimerPeriod, 
ApicTimerFrequency), TimerPeriodDivisor, NULL);
+    DEBUG ((DEBUG_INFO, "%a: InitialCount = %d\n", __func__, InitialCount));
+
     //
     // Program the timer with the new count value
     //
-    InitializeApicTimer (DivideValue, (UINT32)TimerCount, TRUE, 
LOCAL_APIC_TIMER_VECTOR);
+    InitializeApicTimer (Divisor, (UINT32)InitialCount, TRUE, 
LOCAL_APIC_TIMER_VECTOR);
 
     //
     // Enable timer interrupt
diff --git a/UefiCpuPkg/LocalApicTimerDxe/LocalApicTimerDxe.h 
b/UefiCpuPkg/LocalApicTimerDxe/LocalApicTimerDxe.h
index 93706995f8..d7f38a3dc3 100644
--- a/UefiCpuPkg/LocalApicTimerDxe/LocalApicTimerDxe.h
+++ b/UefiCpuPkg/LocalApicTimerDxe/LocalApicTimerDxe.h
@@ -1,7 +1,7 @@
 /** @file
   Private data structures
 
-Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2005 - 2024, Intel Corporation. All rights reserved.<BR>
 Copyright (c) 2019, Citrix Systems, Inc.
 
 SPDX-License-Identifier: BSD-2-Clause-Patent
@@ -16,12 +16,14 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 #include <Protocol/Timer.h>
 
 #include <Register/LocalApic.h>
+#include <Register/Cpuid.h>
 
 #include <Library/UefiBootServicesTableLib.h>
 #include <Library/BaseLib.h>
 #include <Library/DebugLib.h>
 #include <Library/LocalApicLib.h>
 #include <Library/PcdLib.h>
+#include <Library/TimerLib.h>
 
 // The default timer tick duration is set to 10 ms = 100000 100 ns units
 //
diff --git a/UefiCpuPkg/LocalApicTimerDxe/LocalApicTimerDxe.inf 
b/UefiCpuPkg/LocalApicTimerDxe/LocalApicTimerDxe.inf
index 874d58fa17..a468f09566 100644
--- a/UefiCpuPkg/LocalApicTimerDxe/LocalApicTimerDxe.inf
+++ b/UefiCpuPkg/LocalApicTimerDxe/LocalApicTimerDxe.inf
@@ -37,6 +37,6 @@
   gEfiCpuArchProtocolGuid       ## CONSUMES
   gEfiTimerArchProtocolGuid     ## PRODUCES
 [Pcd]
-  gEfiMdePkgTokenSpaceGuid.PcdFSBClock  ## CONSUMES
+  gUefiCpuPkgTokenSpaceGuid.PcdCpuCoreCrystalClockFrequency  ## CONSUMES
 [Depex]
   gEfiCpuArchProtocolGuid
-- 
2.39.1.windows.1



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#113803): https://edk2.groups.io/g/devel/message/113803
Mute This Topic: https://groups.io/mt/103734964/21656
Group Owner: devel+ow...@edk2.groups.io
Unsubscribe: 
https://edk2.groups.io/g/devel/leave/9847357/21656/1706620634/xyzzy 
[arch...@mail-archive.com]
-=-=-=-=-=-=-=-=-=-=-=-


Reply via email to