Since some people disapprove of white space cleanups mixed in regular commits
[coreboot.git] / src / cpu / x86 / tsc / delay_tsc.c
1 #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
8 static unsigned long clocks_per_usec;
9
10 #if (CONFIG_TSC_X86RDTSC_CALIBRATE_WITH_TIMER2 == 1)
11 #define CLOCK_TICK_RATE 1193180U /* Underlying HZ */
12
13 /* ------ Calibrate the TSC -------
14  * 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
21 #define CALIBRATE_INTERVAL ((20*CLOCK_TICK_RATE)/1000) /* 20ms */
22 #define CALIBRATE_DIVISOR  (20*1000) /* 20ms / 20000 == 1usec */
23
24 static 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          */
80 bad_ctc:
81         printk(BIOS_ERR, "bad_ctc\n");
82         return 0;
83 }
84
85 #else /*  CONFIG_TSC_X86RDTSC_CALIBRATE_WITH_TIMER2 */
86
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
91  * we have found on a wide range of machines that this gives us a a
92  * 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.
97  * about the only thing you can do with long longs, it seems,
98  *is return them and assign them.
99  * (and do asm on them, yuck)
100  * so avoid all ops on long longs.
101  */
102 static unsigned long long calibrate_tsc(void)
103 {
104         unsigned long long start, end, delta;
105         unsigned long result, count;
106
107         printk(BIOS_SPEW, "Calibrating delay loop...\n");
108         start = rdtscll();
109         // no udivdi3 because we don't like libgcc. (only in x86emu)
110         // so we count to 1<< 20 and then right shift 20
111         for(count = 0; count < (1<<20); count ++)
112                 inb(0x80);
113         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;
131         printk(BIOS_SPEW, "end %llx, start %llx\n", end, start);
132         printk(BIOS_SPEW, "32-bit delta %ld\n", (unsigned long) delta);
133
134         printk(BIOS_SPEW, "%s 32-bit result is %ld\n",
135                         __func__,
136                         result);
137         return delta;
138 }
139
140
141 #endif /* CONFIG_TSC_X86RDTSC_CALIBRATE_WITH_TIMER2*/
142
143 void init_timer(void)
144 {
145         if (!clocks_per_usec) {
146                 clocks_per_usec = calibrate_tsc();
147                 printk(BIOS_INFO, "clocks_per_usec: %lu\n", clocks_per_usec);
148         }
149 }
150
151 void 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();
164                 count = rdtscll();
165         }
166 }