1 /*
2  * Copyright (C) 2021 Intel Corporation.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <types.h>
8 #include <util.h>
9 #include <asm/cpuid.h>
10 #include <asm/cpu_caps.h>
11 #include <asm/io.h>
12 #include <asm/tsc.h>
13 #include <asm/cpu.h>
14 #include <logmsg.h>
15 #include <acpi.h>
16 
17 #define CAL_MS	10U
18 
19 #define HPET_PERIOD	0x004U
20 #define HPET_CFG	0x010U
21 #define HPET_COUNTER	0x0F0U
22 
23 #define HPET_CFG_ENABLE	0x001UL
24 
25 static uint32_t tsc_khz;
26 static void *hpet_hva;
27 
pit_calibrate_tsc(uint32_t cal_ms_arg)28 static uint64_t pit_calibrate_tsc(uint32_t cal_ms_arg)
29 {
30 #define PIT_TICK_RATE	1193182U
31 #define PIT_TARGET	0x3FFFU
32 #define PIT_MAX_COUNT	0xFFFFU
33 
34 	uint32_t cal_ms = cal_ms_arg;
35 	uint32_t initial_pit;
36 	uint16_t current_pit;
37 	uint32_t max_cal_ms;
38 	uint64_t current_tsc;
39 	uint8_t initial_pit_high, initial_pit_low;
40 
41 	max_cal_ms = ((PIT_MAX_COUNT - PIT_TARGET) * 1000U) / PIT_TICK_RATE;
42 	cal_ms = min(cal_ms, max_cal_ms);
43 
44 	/* Assume the 8254 delivers 18.2 ticks per second when 16 bits fully
45 	 * wrap.  This is about 1.193MHz or a clock period of 0.8384uSec
46 	 */
47 	initial_pit = (cal_ms * PIT_TICK_RATE) / 1000U;
48 	initial_pit += PIT_TARGET;
49 	initial_pit_high = (uint8_t)(initial_pit >> 8U);
50 	initial_pit_low = (uint8_t)initial_pit;
51 
52 	/* Port 0x43 ==> Control word write; Data 0x30 ==> Select Counter 0,
53 	 * Read/Write least significant byte first, mode 0, 16 bits.
54 	 */
55 
56 	pio_write8(0x30U, 0x43U);
57 	pio_write8(initial_pit_low, 0x40U);	/* Write LSB */
58 	pio_write8(initial_pit_high, 0x40U);		/* Write MSB */
59 
60 	current_tsc = rdtsc();
61 
62 	do {
63 		/* Port 0x43 ==> Control word write; 0x00 ==> Select
64 		 * Counter 0, Counter Latch Command, Mode 0; 16 bits
65 		 */
66 		pio_write8(0x00U, 0x43U);
67 
68 		current_pit = (uint16_t)pio_read8(0x40U);	/* Read LSB */
69 		current_pit |= (uint16_t)pio_read8(0x40U) << 8U;	/* Read MSB */
70 		/* Let the counter count down to PIT_TARGET */
71 	} while (current_pit > PIT_TARGET);
72 
73 	current_tsc = rdtsc() - current_tsc;
74 
75 	return (current_tsc / cal_ms) * 1000U;
76 }
77 
hpet_init(void)78 void hpet_init(void)
79 {
80 	uint64_t cfg;
81 
82 	hpet_hva = parse_hpet();
83 	if (hpet_hva != NULL) {
84 		cfg = mmio_read64(hpet_hva + HPET_CFG);
85 		if ((cfg & HPET_CFG_ENABLE) == 0UL) {
86 			cfg |= HPET_CFG_ENABLE;
87 			mmio_write64(cfg, hpet_hva + HPET_CFG);
88 		}
89 	}
90 }
91 
is_hpet_capable(void)92 static inline bool is_hpet_capable(void)
93 {
94 	return (hpet_hva != NULL);
95 }
96 
hpet_read(uint32_t offset)97 static inline uint32_t hpet_read(uint32_t offset)
98 {
99 	return mmio_read32(hpet_hva + offset);
100 }
101 
tsc_read_hpet(uint64_t * p)102 static inline uint64_t tsc_read_hpet(uint64_t *p)
103 {
104 	uint64_t current_tsc;
105 
106 	/* read hpet first */
107 	*p = hpet_read(HPET_COUNTER);
108 	current_tsc = rdtsc();
109 
110 	return current_tsc;
111 }
112 
hpet_calibrate_tsc(uint32_t cal_ms_arg)113 static uint64_t hpet_calibrate_tsc(uint32_t cal_ms_arg)
114 {
115 	uint64_t tsc1, tsc2, hpet1, hpet2;
116 	uint64_t delta_tsc, delta_fs;
117 	uint64_t rflags, tsc_khz;
118 
119 	CPU_INT_ALL_DISABLE(&rflags);
120 	tsc1 = tsc_read_hpet(&hpet1);
121 	pit_calibrate_tsc(cal_ms_arg);
122 	tsc2 = tsc_read_hpet(&hpet2);
123 	CPU_INT_ALL_RESTORE(rflags);
124 
125 	/* in case counter wrap happened in the low 32 bits */
126 	if (hpet2 <= hpet1) {
127 		hpet2 |= (1UL << 32U);
128 	}
129 	delta_fs = (hpet2 - hpet1) * hpet_read(HPET_PERIOD);
130 	delta_tsc = tsc2 - tsc1;
131 	/*
132 	 * FS_PER_S = 10 ^ 15
133 	 *
134 	 * tsc_khz = delta_tsc / (delta_fs / FS_PER_S) / 1000UL;
135 	 *         = delta_tsc / delta_fs * (10 ^ 12)
136 	 *         = (delta_tsc * (10 ^ 6)) / (delta_fs / (10 ^ 6))
137 	 */
138 	tsc_khz = (delta_tsc * 1000000UL) / (delta_fs / 1000000UL);
139 	return tsc_khz * 1000U;
140 }
141 
pit_hpet_calibrate_tsc(uint32_t cal_ms_arg,uint64_t tsc_ref_hz)142 static uint64_t pit_hpet_calibrate_tsc(uint32_t cal_ms_arg, uint64_t tsc_ref_hz)
143 {
144 	uint64_t tsc_hz, delta;
145 
146 	if (is_hpet_capable()) {
147 		tsc_hz = hpet_calibrate_tsc(cal_ms_arg);
148 	} else {
149 		tsc_hz = pit_calibrate_tsc(cal_ms_arg);
150 	}
151 
152 	if (tsc_ref_hz != 0UL) {
153 		delta = (tsc_hz * 100UL) / tsc_ref_hz;
154 		if ((delta < 95UL) || (delta > 105UL)) {
155 			tsc_hz = tsc_ref_hz;
156 		}
157 	}
158 
159 	return tsc_hz;
160 }
161 
162 /*
163  * Determine TSC frequency via CPUID 0x15.
164  */
native_calculate_tsc_cpuid_0x15(void)165 static uint64_t native_calculate_tsc_cpuid_0x15(void)
166 {
167 	uint64_t tsc_hz = 0UL;
168 	const struct cpuinfo_x86 *cpu_info = get_pcpu_info();
169 
170 	if (cpu_info->cpuid_level >= 0x15U) {
171 		uint32_t eax_denominator, ebx_numerator, ecx_hz, reserved;
172 
173 		cpuid_subleaf(0x15U, 0x0U, &eax_denominator, &ebx_numerator,
174 			&ecx_hz, &reserved);
175 
176 		if ((eax_denominator != 0U) && (ebx_numerator != 0U)) {
177 			tsc_hz = ((uint64_t) ecx_hz *
178 				ebx_numerator) / eax_denominator;
179 		}
180 	}
181 
182 	return tsc_hz;
183 }
184 
185 /*
186  * Determine TSC frequency via CPUID 0x16.
187  */
native_calculate_tsc_cpuid_0x16(void)188 static uint64_t native_calculate_tsc_cpuid_0x16(void)
189 {
190 	uint64_t tsc_hz = 0UL;
191 	const struct cpuinfo_x86 *cpu_info = get_pcpu_info();
192 
193 	if (cpu_info->cpuid_level >= 0x16U) {
194 		uint32_t eax_base_mhz, ebx_max_mhz, ecx_bus_mhz, edx;
195 
196 		cpuid_subleaf(0x16U, 0x0U, &eax_base_mhz, &ebx_max_mhz, &ecx_bus_mhz, &edx);
197 		tsc_hz = (uint64_t) eax_base_mhz * 1000000U;
198 	}
199 
200 	return tsc_hz;
201 }
202 
calibrate_tsc(void)203 void calibrate_tsc(void)
204 {
205 	uint64_t tsc_hz;
206 
207 	tsc_hz = native_calculate_tsc_cpuid_0x15();
208 	if (tsc_hz == 0UL) {
209 		tsc_hz = pit_hpet_calibrate_tsc(CAL_MS, native_calculate_tsc_cpuid_0x16());
210 	}
211 	tsc_khz = (uint32_t)(tsc_hz / 1000UL);
212 	pr_acrnlog("%s: tsc_khz = %ld", __func__, tsc_khz);
213 }
214 
get_tsc_khz(void)215 uint32_t get_tsc_khz(void)
216 {
217 	return tsc_khz;
218 }
219 
220 /* external API */
221 
cpu_ticks(void)222 uint64_t cpu_ticks(void)
223 {
224 	return rdtsc();
225 }
226 
cpu_tickrate(void)227 uint32_t cpu_tickrate(void)
228 {
229 	return tsc_khz;
230 }
231