tsc: AMD Family 17h, 19h: compute frequency from Core::X86::Msr:PStateDef
authorcheloha <cheloha@openbsd.org>
Mon, 24 Oct 2022 00:56:33 +0000 (00:56 +0000)
committercheloha <cheloha@openbsd.org>
Mon, 24 Oct 2022 00:56:33 +0000 (00:56 +0000)
Compute the TSC frequency on AMD family 17h and 19h CPUs using the
PStateDef MSRs.

   Link 1: https://marc.info/?l=openbsd-tech&m=166394236029484&w=2
   Link 2: https://marc.info/?l=openbsd-tech&m=166446065916283&w=2
Test list: https://marc.info/?l=openbsd-tech&m=166646389821326&w=2

Reviewed by kettenis@ using the AMD documents cited in the comments.
Maybe reviewed by mlarkin@?  I can't remember.  He seemed supportive
of the idea at least.

ok kettenis@

sys/arch/amd64/amd64/tsc.c
sys/arch/amd64/include/specialreg.h

index 7e5c414..a3086a4 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: tsc.c,v 1.29 2022/09/22 04:57:08 robert Exp $ */
+/*     $OpenBSD: tsc.c,v 1.30 2022/10/24 00:56:33 cheloha Exp $        */
 /*
  * Copyright (c) 2008 The NetBSD Foundation, Inc.
  * Copyright (c) 2016,2017 Reyk Floeter <reyk@openbsd.org>
@@ -100,6 +100,67 @@ tsc_freq_cpuid(struct cpu_info *ci)
        return (0);
 }
 
+uint64_t
+tsc_freq_msr(struct cpu_info *ci)
+{
+       uint64_t base, def, divisor, multiplier;
+
+       if (strcmp(cpu_vendor, "AuthenticAMD") != 0)
+               return 0;
+
+       /*
+        * All 10h+ CPUs have Core::X86::Msr:HWCR and the TscFreqSel
+        * bit.  If TscFreqSel hasn't been set, the TSC isn't advancing
+        * at the core P0 frequency and we need to calibrate by hand.
+        */
+       if (ci->ci_family < 0x10)
+               return 0;
+       if (!ISSET(rdmsr(MSR_HWCR), HWCR_TSCFREQSEL))
+               return 0;
+
+       /*
+        * In 10h+ CPUs, Core::X86::Msr::PStateDef defines the voltage
+        * and frequency for each core P-state.  We want the P0 frequency.
+        * If the En bit isn't set, the register doesn't define a valid
+        * P-state.
+        */
+       def = rdmsr(MSR_PSTATEDEF(0));
+       if (!ISSET(def, PSTATEDEF_EN))
+               return 0;
+
+       switch (ci->ci_family) {
+       case 0x17:
+       case 0x19:
+               /*
+                * PPR for AMD Family 17h [...]:
+                * Models 01h,08h B2, Rev 3.03, pp. 33, 139-140
+                * Model 18h B1, Rev 3.16, pp. 36, 143-144
+                * Model 60h A1, Rev 3.06, pp. 33, 155-157
+                * Model 71h B0, Rev 3.06, pp. 28, 150-151
+                *
+                * PPR for AMD Family 19h [...]:
+                * Model 21h B0, Rev 3.05, pp. 33, 166-167
+                *
+                * OSRR for AMD Family 17h processors,
+                * Models 00h-2Fh, Rev 3.03, pp. 130-131
+                */
+               base = 200000000;                       /* 200.0 MHz */
+               divisor = (def >> 8) & 0x3f;
+               if (divisor <= 0x07 || divisor >= 0x2d)
+                       return 0;                       /* reserved */
+               if (divisor >= 0x1b && divisor % 2 == 1)
+                       return 0;                       /* reserved */
+               multiplier = def & 0xff;
+               if (multiplier <= 0x0f)
+                       return 0;                       /* reserved */
+               break;
+       default:
+               return 0;
+       }
+
+       return base * multiplier / divisor;
+}
+
 void
 tsc_identify(struct cpu_info *ci)
 {
@@ -118,6 +179,8 @@ tsc_identify(struct cpu_info *ci)
        tsc_is_invariant = 1;
 
        tsc_frequency = tsc_freq_cpuid(ci);
+       if (tsc_frequency == 0)
+               tsc_frequency = tsc_freq_msr(ci);
        if (tsc_frequency > 0)
                delay_init(tsc_delay, 5000);
 }
index e7bc30f..cbde6cf 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: specialreg.h,v 1.94 2022/08/30 17:09:21 dv Exp $      */
+/*     $OpenBSD: specialreg.h,v 1.95 2022/10/24 00:56:33 cheloha Exp $ */
 /*     $NetBSD: specialreg.h,v 1.1 2003/04/26 18:39:48 fvdl Exp $      */
 /*     $NetBSD: x86/specialreg.h,v 1.2 2003/04/25 21:54:30 fvdl Exp $  */
 
  */
 #define        MSR_HWCR        0xc0010015
 #define                HWCR_FFDIS              0x00000040
+#define                HWCR_TSCFREQSEL         0x01000000
+
+#define        MSR_PSTATEDEF(_n)       (0xc0010064 + (_n))
+#define                PSTATEDEF_EN            0x8000000000000000ULL
 
 #define        MSR_NB_CFG      0xc001001f
 #define                NB_CFG_DISIOREQLOCK     0x0000000000000004ULL