summaryrefslogtreecommitdiff
path: root/minix/lib/libsys/arch/i386/tsc_util.c
blob: c4bc19222aca755beaef5ed08596b6ae7f3eda66 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

#include <stdio.h>
#include <time.h>
#include <sys/times.h>
#include <sys/types.h>
#include <minix/u64.h>
#include <minix/config.h>
#include <minix/const.h>
#include <minix/minlib.h>
#include <machine/archtypes.h>

#include "sysutil.h"

#ifndef CONFIG_MAX_CPUS
#define CONFIG_MAX_CPUS 1
#endif

#define MICROHZ		1000000		/* number of micros per second */
#define MICROSPERTICK(h)	(MICROHZ/(h))	/* number of micros per HZ tick */

#define CALIBRATE 						\
	if(!calibrated) {					\
		int r;						\
		if((r=tsc_calibrate()) != OK)			\
			panic("calibrate failed: %d", r); \
	}

static u32_t calib_mhz, Hz = 0;
static int calibrated = 0;

int
tsc_calibrate(void)
{
	struct cpu_info cpu_info[CONFIG_MAX_CPUS];

	/* Get HZ. */
	Hz = sys_hz();

	/* Obtain CPU frequency from kernel */
	if (sys_getcpuinfo(&cpu_info)) {
		printf("tsc_calibrate: cannot get cpu info\n");
		return -1;
	}
	
	/* For now, use the frequency of the first CPU; everything here will 
	 * break down in case we get scheduled on multiple CPUs with different 
	 * frequencies regardless
	 */
	calib_mhz = cpu_info[0].freq;
	calibrated = 1;

	return OK;
}

int
micro_delay(u32_t micros)
{
	u64_t now, end;

	/* Start of delay. */
	read_tsc_64(&now);

	CALIBRATE;

	/* We have to know when to end the delay. */
	end = now + ((u64_t)micros * calib_mhz);

	/* If we have to wait for at least one HZ tick, use the regular
	 * tickdelay first. Round downwards on purpose, so the average
	 * half-tick we wait short (depending on where in the current tick
	 * we call tickdelay). We can correct for both overhead of tickdelay
	 * itself and the short wait in the busywait later.
	 */
	if(micros >= MICROSPERTICK(Hz))
		tickdelay(micros*Hz/MICROHZ);

	/* Wait (the rest) of the delay time using busywait. */
	while(now < end)
		read_tsc_64(&now);

	return OK;
}

u32_t tsc_64_to_micros(u64_t tsc)
{
	u64_t tmp;

	CALIBRATE;

	tmp = tsc / calib_mhz;
	if (ex64hi(tmp)) {
		printf("tsc_64_to_micros: more than 2^32ms\n");
		return ~0UL;
	} else {
		return ex64lo(tmp);
	}
}

u32_t tsc_to_micros(u32_t low, u32_t high)
{
	return tsc_64_to_micros(make64(low, high));
}

u32_t tsc_get_khz(void)
{
	CALIBRATE;

	return calib_mhz * 1000;
}

#define frclock_64_to_micros tsc_64_to_micros
#define read_frclock_64 read_tsc_64

u64_t delta_frclock_64(u64_t base, u64_t cur)
{
        return cur - base;
}