xref: /openbmc/linux/tools/perf/arch/x86/util/tsc.c (revision 2a9eb57e)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/types.h>
3 #include <math.h>
4 #include <string.h>
5 
6 #include "../../../util/debug.h"
7 #include "../../../util/tsc.h"
8 #include "cpuid.h"
9 
10 u64 rdtsc(void)
11 {
12 	unsigned int low, high;
13 
14 	asm volatile("rdtsc" : "=a" (low), "=d" (high));
15 
16 	return low | ((u64)high) << 32;
17 }
18 
19 /*
20  * Derive the TSC frequency in Hz from the /proc/cpuinfo, for example:
21  * ...
22  * model name      : Intel(R) Xeon(R) Gold 6154 CPU @ 3.00GHz
23  * ...
24  * will return 3000000000.
25  */
26 static double cpuinfo_tsc_freq(void)
27 {
28 	double result = 0;
29 	FILE *cpuinfo;
30 	char *line = NULL;
31 	size_t len = 0;
32 
33 	cpuinfo = fopen("/proc/cpuinfo", "r");
34 	if (!cpuinfo) {
35 		pr_err("Failed to read /proc/cpuinfo for TSC frequency");
36 		return NAN;
37 	}
38 	while (getline(&line, &len, cpuinfo) > 0) {
39 		if (!strncmp(line, "model name", 10)) {
40 			char *pos = strstr(line + 11, " @ ");
41 
42 			if (pos && sscanf(pos, " @ %lfGHz", &result) == 1) {
43 				result *= 1000000000;
44 				goto out;
45 			}
46 		}
47 	}
48 out:
49 	if (fpclassify(result) == FP_ZERO)
50 		pr_err("Failed to find TSC frequency in /proc/cpuinfo");
51 
52 	free(line);
53 	fclose(cpuinfo);
54 	return result;
55 }
56 
57 double arch_get_tsc_freq(void)
58 {
59 	unsigned int a, b, c, d, lvl;
60 	static bool cached;
61 	static double tsc;
62 	char vendor[16];
63 
64 	if (cached)
65 		return tsc;
66 
67 	cached = true;
68 	get_cpuid_0(vendor, &lvl);
69 	if (!strstr(vendor, "Intel"))
70 		return 0;
71 
72 	/*
73 	 * Don't support Time Stamp Counter and
74 	 * Nominal Core Crystal Clock Information Leaf.
75 	 */
76 	if (lvl < 0x15) {
77 		tsc = cpuinfo_tsc_freq();
78 		return tsc;
79 	}
80 
81 	cpuid(0x15, 0, &a, &b, &c, &d);
82 	/* TSC frequency is not enumerated */
83 	if (!a || !b || !c) {
84 		tsc = cpuinfo_tsc_freq();
85 		return tsc;
86 	}
87 
88 	tsc = (double)c * (double)b / (double)a;
89 	return tsc;
90 }
91