blob: e4993d0dd5afe8b8f525b7ab0660e81bfec05dda [file] [log] [blame]
Eric Biedermanfcd5ace2004-10-14 19:29:29 +00001#include <console/console.h>
2#include <arch/io.h>
3#include <cpu/x86/msr.h>
4#include <cpu/x86/tsc.h>
5#include <smp/spinlock.h>
6#include <delay.h>
7
8static unsigned long clocks_per_usec;
9
Patrick Georgia4c0a1d2010-09-08 10:58:02 +000010#if !CONFIG_TSC_CALIBRATE_WITH_IO
Eric Biedermanfcd5ace2004-10-14 19:29:29 +000011#define CLOCK_TICK_RATE 1193180U /* Underlying HZ */
12
Stefan Reinauer14e22772010-04-27 06:56:47 +000013/* ------ Calibrate the TSC -------
Eric Biedermanfcd5ace2004-10-14 19:29:29 +000014 * Too much 64-bit arithmetic here to do this cleanly in C, and for
15 * accuracy's sake we want to keep the overhead on the CTC speaker (channel 2)
16 * output busy loop as low as possible. We avoid reading the CTC registers
17 * directly because of the awkward 8-bit access mechanism of the 82C54
18 * device.
19 */
20
Kevin O'Connor3e4daf12010-09-07 07:53:26 +000021#define CALIBRATE_INTERVAL ((2*CLOCK_TICK_RATE)/1000) /* 2ms */
22#define CALIBRATE_DIVISOR (2*1000) /* 2ms / 2000 == 1usec */
Eric Biedermanfcd5ace2004-10-14 19:29:29 +000023
24static unsigned long long calibrate_tsc(void)
25{
26 /* Set the Gate high, disable speaker */
27 outb((inb(0x61) & ~0x02) | 0x01, 0x61);
28
29 /*
30 * Now let's take care of CTC channel 2
31 *
32 * Set the Gate high, program CTC channel 2 for mode 0,
33 * (interrupt on terminal count mode), binary count,
34 * load 5 * LATCH count, (LSB and MSB) to begin countdown.
35 */
36 outb(0xb0, 0x43); /* binary, mode 0, LSB/MSB, Ch 2 */
37 outb(CALIBRATE_INTERVAL & 0xff, 0x42); /* LSB of count */
38 outb(CALIBRATE_INTERVAL >> 8, 0x42); /* MSB of count */
39
40 {
41 tsc_t start;
42 tsc_t end;
43 unsigned long count;
44
45 start = rdtsc();
46 count = 0;
47 do {
48 count++;
49 } while ((inb(0x61) & 0x20) == 0);
50 end = rdtsc();
51
52 /* Error: ECTCNEVERSET */
53 if (count <= 1)
54 goto bad_ctc;
55
56 /* 64-bit subtract - gcc just messes up with long longs */
57 __asm__("subl %2,%0\n\t"
58 "sbbl %3,%1"
59 :"=a" (end.lo), "=d" (end.hi)
60 :"g" (start.lo), "g" (start.hi),
61 "0" (end.lo), "1" (end.hi));
62
63 /* Error: ECPUTOOFAST */
64 if (end.hi)
65 goto bad_ctc;
66
67
68 /* Error: ECPUTOOSLOW */
69 if (end.lo <= CALIBRATE_DIVISOR)
70 goto bad_ctc;
71
72 return (end.lo + CALIBRATE_DIVISOR -1)/CALIBRATE_DIVISOR;
73 }
74
75 /*
76 * The CTC wasn't reliable: we got a hit on the very first read,
77 * or the CPU was so fast/slow that the quotient wouldn't fit in
78 * 32 bits..
79 */
80bad_ctc:
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +000081 printk(BIOS_ERR, "bad_ctc\n");
Eric Biedermanfcd5ace2004-10-14 19:29:29 +000082 return 0;
83}
84
Patrick Georgia4c0a1d2010-09-08 10:58:02 +000085#else /* CONFIG_TSC_CALIBRATE_WITH_IO */
Eric Biedermanfcd5ace2004-10-14 19:29:29 +000086
87/*
88 * this is the "no timer2" version.
89 * to calibrate tsc, we get a TSC reading, then do 1,000,000 outbs to port 0x80
90 * then we read TSC again, and divide the difference by 1,000,000
Stefan Reinauer14e22772010-04-27 06:56:47 +000091 * we have found on a wide range of machines that this gives us a a
Eric Biedermanfcd5ace2004-10-14 19:29:29 +000092 * good microsecond value
93 * to +- 10%. On a dual AMD 1.6 Ghz box, it gives us .97 microseconds, and on a
94 * 267 Mhz. p5, it gives us 1.1 microseconds.
95 * also, since gcc now supports long long, we use that.
96 * also no unsigned long long / operator, so we play games.
Stefan Reinauer14e22772010-04-27 06:56:47 +000097 * about the only thing you can do with long longs, it seems,
Eric Biedermanfcd5ace2004-10-14 19:29:29 +000098 *is return them and assign them.
99 * (and do asm on them, yuck)
100 * so avoid all ops on long longs.
101 */
102static unsigned long long calibrate_tsc(void)
103{
104 unsigned long long start, end, delta;
Stefan Reinauer0dff6e32007-10-23 22:17:45 +0000105 unsigned long result, count;
Stefan Reinauer14e22772010-04-27 06:56:47 +0000106
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000107 printk(BIOS_SPEW, "Calibrating delay loop...\n");
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000108 start = rdtscll();
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +0000109 // no udivdi3 because we don't like libgcc. (only in x86emu)
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000110 // so we count to 1<< 20 and then right shift 20
111 for(count = 0; count < (1<<20); count ++)
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +0000112 inb(0x80);
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000113 end = rdtscll();
114
115#if 0
116 // make delta be (endhigh - starthigh) + (endlow - startlow)
117 // but >> 20
118 // do it this way to avoid gcc warnings.
119 start = tsc_start.hi;
120 start <<= 32;
121 start |= start.lo;
122 end = tsc_end.hi;
123 end <<= 32;
124 end |= tsc_end.lo;
125#endif
126 delta = end - start;
127 // at this point we have a delta for 1,000,000 outbs. Now rescale for one microsecond.
128 delta >>= 20;
129 // save this for microsecond timing.
130 result = delta;
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000131 printk(BIOS_SPEW, "end %llx, start %llx\n", end, start);
132 printk(BIOS_SPEW, "32-bit delta %ld\n", (unsigned long) delta);
Stefan Reinauer14e22772010-04-27 06:56:47 +0000133
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000134 printk(BIOS_SPEW, "%s 32-bit result is %ld\n",
Myles Watson552b3272009-02-12 21:30:06 +0000135 __func__,
Jonathan McDowell1718c472005-09-14 16:33:10 +0000136 result);
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000137 return delta;
138}
139
140
Patrick Georgi17daf9a2010-09-09 22:12:40 +0000141#endif /* CONFIG_TSC_CALIBRATE_WITH_IO */
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000142
143void init_timer(void)
144{
145 if (!clocks_per_usec) {
146 clocks_per_usec = calibrate_tsc();
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000147 printk(BIOS_INFO, "clocks_per_usec: %lu\n", clocks_per_usec);
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000148 }
149}
150
151void udelay(unsigned us)
152{
153 unsigned long long count;
154 unsigned long long stop;
155 unsigned long long clocks;
156
157 init_timer();
158 clocks = us;
159 clocks *= clocks_per_usec;
160 count = rdtscll();
161 stop = clocks + count;
162 while(stop > count) {
163 cpu_relax();
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000164 count = rdtscll();
165 }
166}
Aaron Durbine8501642013-04-29 22:22:55 -0500167
168#if CONFIG_TSC_MONOTONIC_TIMER
169#include <timer.h>
170
171static struct monotonic_counter {
172 int initialized;
173 struct mono_time time;
174 uint64_t last_value;
175} mono_counter;
176
177void timer_monotonic_get(struct mono_time *mt)
178{
179 uint64_t current_tick;
180 uint64_t ticks_elapsed;
181
182 if (!mono_counter.initialized) {
183 init_timer();
184 mono_counter.last_value = rdtscll();
185 mono_counter.initialized = 1;
186 }
187
188 current_tick = rdtscll();
189 ticks_elapsed = current_tick - mono_counter.last_value;
190
191 /* Update current time and tick values only if a full tick occurred. */
192 if (ticks_elapsed >= clocks_per_usec) {
193 uint64_t usecs_elapsed;
194
195 usecs_elapsed = ticks_elapsed / clocks_per_usec;
196 mono_time_add_usecs(&mono_counter.time, (long)usecs_elapsed);
197 mono_counter.last_value = current_tick;
198 }
199
200 /* Save result. */
201 *mt = mono_counter.time;
202}
203#endif