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