24
24
#include <grub/time.h>
25
25
#include <grub/misc.h>
26
26
#include <grub/i386/tsc.h>
27
#include <grub/i386/cpuid.h>
28
#ifdef GRUB_MACHINE_XEN
27
31
#include <grub/i386/pit.h>
33
#include <grub/cpu/io.h>
29
35
/* This defines the value TSC had at the epoch (that is, when we calibrated it). */
30
36
static grub_uint64_t tsc_boot_time;
32
/* Calibrated TSC rate. (In TSC ticks per millisecond.) */
33
static grub_uint64_t tsc_ticks_per_ms;
38
/* Calibrated TSC rate. (In ms per 2^32 ticks) */
39
/* We assume that the tick is less than 1 ms and hence this value fits
41
grub_uint32_t grub_tsc_rate;
43
/* Read the TSC value, which increments with each CPU clock cycle. */
44
static __inline grub_uint64_t
48
grub_uint32_t a,b,c,d;
50
/* The CPUID instruction is a 'serializing' instruction, and
51
avoids out-of-order execution of the RDTSC instruction. */
52
grub_cpuid (0,a,b,c,d);
53
/* Read TSC value. We cannot use "=A", since this would use
55
__asm__ __volatile__ ("rdtsc":"=a" (lo), "=d" (hi));
57
return (((grub_uint64_t) hi) << 32) | lo;
61
grub_cpu_is_tsc_supported (void)
63
grub_uint32_t a,b,c,d;
64
if (! grub_cpu_is_cpuid_supported ())
67
grub_cpuid(1,a,b,c,d);
69
return (d & (1 << 4)) != 0;
72
#ifndef GRUB_MACHINE_XEN
75
grub_pit_wait (grub_uint16_t tics)
77
/* Disable timer2 gate and speaker. */
78
grub_outb (grub_inb (GRUB_PIT_SPEAKER_PORT)
79
& ~ (GRUB_PIT_SPK_DATA | GRUB_PIT_SPK_TMR2),
80
GRUB_PIT_SPEAKER_PORT);
83
grub_outb (GRUB_PIT_CTRL_SELECT_2 | GRUB_PIT_CTRL_READLOAD_WORD,
85
grub_outb (tics & 0xff, GRUB_PIT_COUNTER_2);
86
grub_outb (tics >> 8, GRUB_PIT_COUNTER_2);
88
/* Enable timer2 gate, keep speaker disabled. */
89
grub_outb ((grub_inb (GRUB_PIT_SPEAKER_PORT) & ~ GRUB_PIT_SPK_DATA)
91
GRUB_PIT_SPEAKER_PORT);
94
while ((grub_inb (GRUB_PIT_SPEAKER_PORT) & GRUB_PIT_SPK_TMR2_LATCH) == 0x00);
96
/* Disable timer2 gate and speaker. */
97
grub_outb (grub_inb (GRUB_PIT_SPEAKER_PORT)
98
& ~ (GRUB_PIT_SPK_DATA | GRUB_PIT_SPK_TMR2),
99
GRUB_PIT_SPEAKER_PORT);
37
104
grub_tsc_get_time_ms (void)
39
return tsc_boot_time + grub_divmod64 (grub_get_tsc (), tsc_ticks_per_ms, 0);
106
grub_uint64_t a = grub_get_tsc () - tsc_boot_time;
107
grub_uint64_t ah = a >> 32;
108
grub_uint64_t al = a & 0xffffffff;
110
return ((al * grub_tsc_rate) >> 32) + ah * grub_tsc_rate;
43
/* How many RTC ticks to use for calibration loop. (>= 1) */
44
#define CALIBRATION_TICKS 2
113
#ifndef GRUB_MACHINE_XEN
46
114
/* Calibrate the TSC based on the RTC. */
48
116
calibrate_tsc (void)
50
118
/* First calibrate the TSC rate (relative, not absolute time). */
51
grub_uint64_t start_tsc;
52
119
grub_uint64_t end_tsc;
54
start_tsc = grub_get_tsc ();
121
tsc_boot_time = grub_get_tsc ();
55
122
grub_pit_wait (0xffff);
56
123
end_tsc = grub_get_tsc ();
58
tsc_ticks_per_ms = grub_divmod64 (end_tsc - start_tsc, 55, 0);
125
grub_tsc_rate = grub_divmod64 ((55ULL << 32), end_tsc - tsc_boot_time, 0);
62
130
grub_tsc_init (void)
132
#ifdef GRUB_MACHINE_XEN
134
tsc_boot_time = grub_get_tsc ();
135
t = grub_xen_shared_info->vcpu_info[0].time.tsc_to_system_mul;
136
if (grub_xen_shared_info->vcpu_info[0].time.tsc_shift > 0)
137
t <<= grub_xen_shared_info->vcpu_info[0].time.tsc_shift;
139
t >>= -grub_xen_shared_info->vcpu_info[0].time.tsc_shift;
140
grub_tsc_rate = grub_divmod64 (t, 1000000, 0);
141
grub_install_get_time_ms (grub_tsc_get_time_ms);
64
143
if (grub_cpu_is_tsc_supported ())
66
tsc_boot_time = grub_get_tsc ();
68
146
grub_install_get_time_ms (grub_tsc_get_time_ms);