xref: /minix3/minix/lib/libsys/arch/i386/tsc_util.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
1*433d6423SLionel Sambuc 
2*433d6423SLionel Sambuc #include <stdio.h>
3*433d6423SLionel Sambuc #include <time.h>
4*433d6423SLionel Sambuc #include <sys/times.h>
5*433d6423SLionel Sambuc #include <sys/types.h>
6*433d6423SLionel Sambuc #include <minix/u64.h>
7*433d6423SLionel Sambuc #include <minix/config.h>
8*433d6423SLionel Sambuc #include <minix/const.h>
9*433d6423SLionel Sambuc #include <minix/minlib.h>
10*433d6423SLionel Sambuc #include <machine/archtypes.h>
11*433d6423SLionel Sambuc 
12*433d6423SLionel Sambuc #include "sysutil.h"
13*433d6423SLionel Sambuc 
14*433d6423SLionel Sambuc #ifndef CONFIG_MAX_CPUS
15*433d6423SLionel Sambuc #define CONFIG_MAX_CPUS 1
16*433d6423SLionel Sambuc #endif
17*433d6423SLionel Sambuc 
18*433d6423SLionel Sambuc #define MICROHZ		1000000		/* number of micros per second */
19*433d6423SLionel Sambuc #define MICROSPERTICK(h)	(MICROHZ/(h))	/* number of micros per HZ tick */
20*433d6423SLionel Sambuc 
21*433d6423SLionel Sambuc #define CALIBRATE 						\
22*433d6423SLionel Sambuc 	if(!calibrated) {					\
23*433d6423SLionel Sambuc 		int r;						\
24*433d6423SLionel Sambuc 		if((r=tsc_calibrate()) != OK)			\
25*433d6423SLionel Sambuc 			panic("calibrate failed: %d", r); \
26*433d6423SLionel Sambuc 	}
27*433d6423SLionel Sambuc 
28*433d6423SLionel Sambuc static u32_t calib_mhz, Hz = 0;
29*433d6423SLionel Sambuc static int calibrated = 0;
30*433d6423SLionel Sambuc 
31*433d6423SLionel Sambuc int
tsc_calibrate(void)32*433d6423SLionel Sambuc tsc_calibrate(void)
33*433d6423SLionel Sambuc {
34*433d6423SLionel Sambuc 	struct cpu_info cpu_info[CONFIG_MAX_CPUS];
35*433d6423SLionel Sambuc 
36*433d6423SLionel Sambuc 	/* Get HZ. */
37*433d6423SLionel Sambuc 	Hz = sys_hz();
38*433d6423SLionel Sambuc 
39*433d6423SLionel Sambuc 	/* Obtain CPU frequency from kernel */
40*433d6423SLionel Sambuc 	if (sys_getcpuinfo(&cpu_info)) {
41*433d6423SLionel Sambuc 		printf("tsc_calibrate: cannot get cpu info\n");
42*433d6423SLionel Sambuc 		return -1;
43*433d6423SLionel Sambuc 	}
44*433d6423SLionel Sambuc 
45*433d6423SLionel Sambuc 	/* For now, use the frequency of the first CPU; everything here will
46*433d6423SLionel Sambuc 	 * break down in case we get scheduled on multiple CPUs with different
47*433d6423SLionel Sambuc 	 * frequencies regardless
48*433d6423SLionel Sambuc 	 */
49*433d6423SLionel Sambuc 	calib_mhz = cpu_info[0].freq;
50*433d6423SLionel Sambuc 	calibrated = 1;
51*433d6423SLionel Sambuc 
52*433d6423SLionel Sambuc 	return OK;
53*433d6423SLionel Sambuc }
54*433d6423SLionel Sambuc 
55*433d6423SLionel Sambuc int
micro_delay(u32_t micros)56*433d6423SLionel Sambuc micro_delay(u32_t micros)
57*433d6423SLionel Sambuc {
58*433d6423SLionel Sambuc 	u64_t now, end;
59*433d6423SLionel Sambuc 
60*433d6423SLionel Sambuc 	/* Start of delay. */
61*433d6423SLionel Sambuc 	read_tsc_64(&now);
62*433d6423SLionel Sambuc 
63*433d6423SLionel Sambuc 	CALIBRATE;
64*433d6423SLionel Sambuc 
65*433d6423SLionel Sambuc 	/* We have to know when to end the delay. */
66*433d6423SLionel Sambuc 	end = now + ((u64_t)micros * calib_mhz);
67*433d6423SLionel Sambuc 
68*433d6423SLionel Sambuc 	/* If we have to wait for at least one HZ tick, use the regular
69*433d6423SLionel Sambuc 	 * tickdelay first. Round downwards on purpose, so the average
70*433d6423SLionel Sambuc 	 * half-tick we wait short (depending on where in the current tick
71*433d6423SLionel Sambuc 	 * we call tickdelay). We can correct for both overhead of tickdelay
72*433d6423SLionel Sambuc 	 * itself and the short wait in the busywait later.
73*433d6423SLionel Sambuc 	 */
74*433d6423SLionel Sambuc 	if(micros >= MICROSPERTICK(Hz))
75*433d6423SLionel Sambuc 		tickdelay(micros*Hz/MICROHZ);
76*433d6423SLionel Sambuc 
77*433d6423SLionel Sambuc 	/* Wait (the rest) of the delay time using busywait. */
78*433d6423SLionel Sambuc 	while(now < end)
79*433d6423SLionel Sambuc 		read_tsc_64(&now);
80*433d6423SLionel Sambuc 
81*433d6423SLionel Sambuc 	return OK;
82*433d6423SLionel Sambuc }
83*433d6423SLionel Sambuc 
tsc_64_to_micros(u64_t tsc)84*433d6423SLionel Sambuc u32_t tsc_64_to_micros(u64_t tsc)
85*433d6423SLionel Sambuc {
86*433d6423SLionel Sambuc 	u64_t tmp;
87*433d6423SLionel Sambuc 
88*433d6423SLionel Sambuc 	CALIBRATE;
89*433d6423SLionel Sambuc 
90*433d6423SLionel Sambuc 	tmp = tsc / calib_mhz;
91*433d6423SLionel Sambuc 	if (ex64hi(tmp)) {
92*433d6423SLionel Sambuc 		printf("tsc_64_to_micros: more than 2^32ms\n");
93*433d6423SLionel Sambuc 		return ~0UL;
94*433d6423SLionel Sambuc 	} else {
95*433d6423SLionel Sambuc 		return ex64lo(tmp);
96*433d6423SLionel Sambuc 	}
97*433d6423SLionel Sambuc }
98*433d6423SLionel Sambuc 
tsc_to_micros(u32_t low,u32_t high)99*433d6423SLionel Sambuc u32_t tsc_to_micros(u32_t low, u32_t high)
100*433d6423SLionel Sambuc {
101*433d6423SLionel Sambuc 	return tsc_64_to_micros(make64(low, high));
102*433d6423SLionel Sambuc }
103*433d6423SLionel Sambuc 
tsc_get_khz(void)104*433d6423SLionel Sambuc u32_t tsc_get_khz(void)
105*433d6423SLionel Sambuc {
106*433d6423SLionel Sambuc 	CALIBRATE;
107*433d6423SLionel Sambuc 
108*433d6423SLionel Sambuc 	return calib_mhz * 1000;
109*433d6423SLionel Sambuc }
110*433d6423SLionel Sambuc 
111*433d6423SLionel Sambuc #define frclock_64_to_micros tsc_64_to_micros
112*433d6423SLionel Sambuc #define read_frclock_64 read_tsc_64
113*433d6423SLionel Sambuc 
delta_frclock_64(u64_t base,u64_t cur)114*433d6423SLionel Sambuc u64_t delta_frclock_64(u64_t base, u64_t cur)
115*433d6423SLionel Sambuc {
116*433d6423SLionel Sambuc         return cur - base;
117*433d6423SLionel Sambuc }
118*433d6423SLionel Sambuc 
119