public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Ni, Ray" <ray.ni@intel.com>
To: devel@edk2.groups.io
Cc: Michael D Kinney <michael.d.kinney@intel.com>,
	Nate DeSimone <nathaniel.l.desimone@intel.com>,
	Laszlo Ersek <lersek@redhat.com>,
	Rahul Kumar <rahul1.kumar@intel.com>,
	Gerd Hoffmann <kraxel@redhat.com>
Subject: [edk2-devel] [PATCH 4/6] UefiCpuPkg/LocalApicTimerDxe: Enhance Timer Frequency calculation logic
Date: Mon, 15 Jan 2024 16:03:23 +0800	[thread overview]
Message-ID: <20240115080325.147-5-ray.ni@intel.com> (raw)
In-Reply-To: <20240115080325.147-1-ray.ni@intel.com>

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.ni@intel.com>
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Nate DeSimone <nathaniel.l.desimone@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Rahul Kumar <rahul1.kumar@intel.com>
Cc: Gerd Hoffmann <kraxel@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/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/leave/12367111/7686176/1913456212/xyzzy [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



  parent reply	other threads:[~2024-01-16  5:51 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-01-15  8:03 [edk2-devel] Add LocalApicTimerDxe driver in UefiCpuPkg Ni, Ray
2024-01-15  8:03 ` [edk2-devel] [PATCH 1/6] UefiCpuPkg/LocalApicTimerDxe: Duplicate OvmfPkg/LocalApicTimerDxe driver Ni, Ray
2024-01-15  8:59   ` Pedro Falcato
2024-01-15 18:10     ` Michael D Kinney
2024-01-16 10:02       ` Laszlo Ersek
2024-01-16 17:17         ` Michael D Kinney
2024-01-15 19:30     ` Laszlo Ersek
2024-01-16  8:47       ` Gerd Hoffmann
2024-01-16  9:48         ` Michael Brown
2024-01-16 14:34           ` Laszlo Ersek
2024-01-16 15:16             ` Michael Brown
2024-01-16 15:37               ` Laszlo Ersek
2024-01-17  7:11                 ` Ni, Ray
2024-01-17 10:46                   ` Michael Brown
2024-01-19 13:14                     ` Ni, Ray
2024-01-19 17:42                       ` Michael Brown
2024-01-19 23:44                         ` Ni, Ray
2024-01-20  0:49                           ` Michael Brown
2024-01-22 10:15                         ` Gerd Hoffmann
2024-01-15  8:03 ` [edk2-devel] [PATCH 2/6] UefiCpuPkg/LocalApicTimerDxe: Remove NestedInterruptTplLib call Ni, Ray
2024-01-15  8:03 ` [edk2-devel] [PATCH 3/6] UefiCpuPkg: Add LocalApicTimerDxe driver in DSC file Ni, Ray
2024-01-15  8:03 ` Ni, Ray [this message]
2024-01-15  8:03 ` [edk2-devel] [PATCH 5/6] UefiCpuPkg/LocalApicTimerDxe: warn if APIC Timer is used by other code Ni, Ray
2024-01-15  8:03 ` [edk2-devel] [PATCH 6/6] UefiCpuPkg/LocalApicTimerDxe: Passing fixed timer period is not a bug Ni, Ray

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240115080325.147-5-ray.ni@intel.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox