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