1 /* kern/i386/tsc.c - x86 TSC time source implementation
2 * Requires Pentium or better x86 CPU that supports the RDTSC instruction.
3 * This module uses the RTC (via grub_get_rtc()) to calibrate the TSC to
6 * GRUB -- GRand Unified Bootloader
7 * Copyright (C) 2008 Free Software Foundation, Inc.
9 * GRUB is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
14 * GRUB is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
23 #include <grub/types.h>
24 #include <grub/time.h>
25 #include <grub/misc.h>
26 #include <grub/i386/tsc.h>
27 #include <grub/i386/cpuid.h>
28 #include <grub/i386/pit.h>
29 #include <grub/cpu/io.h>
31 /* This defines the value TSC had at the epoch (that is, when we calibrated it). */
32 static grub_uint64_t tsc_boot_time
;
34 /* Calibrated TSC rate. (In ms per 2^32 ticks) */
35 /* We assume that the tick is less than 1 ms and hence this value fits
37 grub_uint32_t grub_tsc_rate
;
39 /* Read the TSC value, which increments with each CPU clock cycle. */
40 static __inline grub_uint64_t
45 /* The CPUID instruction is a 'serializing' instruction, and
46 avoids out-of-order execution of the RDTSC instruction. */
48 __asm__
__volatile__ ("xorl %%eax, %%eax\n\t"
60 :::"%rax", "%rcx", "%rdx");
62 __asm__
__volatile__ ("xorl %%eax, %%eax\n\t"
63 "cpuid":::"%rax", "%rbx", "%rcx", "%rdx");
65 /* Read TSC value. We cannot use "=A", since this would use
67 __asm__
__volatile__ ("rdtsc":"=a" (lo
), "=d" (hi
));
69 return (((grub_uint64_t
) hi
) << 32) | lo
;
73 grub_cpu_is_tsc_supported (void)
75 if (! grub_cpu_is_cpuid_supported ())
78 grub_uint32_t features
;
80 __asm__ ("movl $1, %%eax\n\t"
94 : /* Clobbered: */ "%rax", "%rcx");
96 __asm__ ("movl $1, %%eax\n\t"
100 : /* Clobbered: */ "%rax", "%rbx", "%rcx");
102 return (features
& (1 << 4)) != 0;
106 grub_pit_wait (grub_uint16_t tics
)
108 /* Disable timer2 gate and speaker. */
109 grub_outb (grub_inb (GRUB_PIT_SPEAKER_PORT
)
110 & ~ (GRUB_PIT_SPK_DATA
| GRUB_PIT_SPK_TMR2
),
111 GRUB_PIT_SPEAKER_PORT
);
114 grub_outb (GRUB_PIT_CTRL_SELECT_2
| GRUB_PIT_CTRL_READLOAD_WORD
,
116 grub_outb (tics
& 0xff, GRUB_PIT_COUNTER_2
);
117 grub_outb (tics
>> 8, GRUB_PIT_COUNTER_2
);
119 /* Enable timer2 gate, keep speaker disabled. */
120 grub_outb ((grub_inb (GRUB_PIT_SPEAKER_PORT
) & ~ GRUB_PIT_SPK_DATA
)
122 GRUB_PIT_SPEAKER_PORT
);
125 while ((grub_inb (GRUB_PIT_SPEAKER_PORT
) & GRUB_PIT_SPK_TMR2_LATCH
) == 0x00);
127 /* Disable timer2 gate and speaker. */
128 grub_outb (grub_inb (GRUB_PIT_SPEAKER_PORT
)
129 & ~ (GRUB_PIT_SPK_DATA
| GRUB_PIT_SPK_TMR2
),
130 GRUB_PIT_SPEAKER_PORT
);
134 grub_tsc_get_time_ms (void)
136 grub_uint64_t a
= grub_get_tsc () - tsc_boot_time
;
137 grub_uint64_t ah
= a
>> 32;
138 grub_uint64_t al
= a
& 0xffffffff;
140 return ((al
* grub_tsc_rate
) >> 32) + ah
* grub_tsc_rate
;
143 /* Calibrate the TSC based on the RTC. */
147 /* First calibrate the TSC rate (relative, not absolute time). */
148 grub_uint64_t end_tsc
;
150 tsc_boot_time
= grub_get_tsc ();
151 grub_pit_wait (0xffff);
152 end_tsc
= grub_get_tsc ();
154 grub_tsc_rate
= grub_divmod64 ((55ULL << 32), end_tsc
- tsc_boot_time
, 0);
160 if (grub_cpu_is_tsc_supported ())
163 grub_install_get_time_ms (grub_tsc_get_time_ms
);
167 #if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_IEEE1275)
168 grub_install_get_time_ms (grub_rtc_get_time_ms
);
170 grub_fatal ("no TSC found");