xref: /haiku/src/system/boot/platform/bios_ia32/cpu.cpp (revision 93aeb8c3bc3f13cb1f282e3e749258a23790d947)
1 /*
2  * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * calculate_cpu_conversion_factor() was written by Travis Geiselbrecht and
6  * licensed under the NewOS license.
7  */
8 
9 
10 #include "cpu.h"
11 
12 #include <OS.h>
13 #include <boot/platform.h>
14 #include <boot/stdio.h>
15 #include <boot/kernel_args.h>
16 #include <boot/stage2.h>
17 #include <arch/cpu.h>
18 #include <arch_kernel.h>
19 
20 #include <string.h>
21 
22 
23 //#define TRACE_CPU
24 #ifdef TRACE_CPU
25 #	define TRACE(x) dprintf x
26 #else
27 #	define TRACE(x) ;
28 #endif
29 
30 
31 extern "C" uint64 rdtsc();
32 
33 uint32 gTimeConversionFactor;
34 
35 #define TIMER_CLKNUM_HZ (14318180/12)
36 
37 
38 static void
39 calculate_cpu_conversion_factor()
40 {
41 	uint32 s_low, s_high;
42 	uint32 low, high;
43 	uint32 expired;
44 	uint64 t1, t2;
45 	uint64 p1, p2, p3;
46 	double r1, r2, r3;
47 
48 	out8(0x34, 0x43);	/* program the timer to count down mode */
49 	out8(0xff, 0x40);	/* low and then high */
50 	out8(0xff, 0x40);
51 
52 	/* quick sample */
53 quick_sample:
54 	do {
55 		out8(0x00, 0x43); /* latch counter value */
56 		s_low = in8(0x40);
57 		s_high = in8(0x40);
58 	} while(s_high != 255);
59 	t1 = rdtsc();
60 	do {
61 		out8(0x00, 0x43); /* latch counter value */
62 		low = in8(0x40);
63 		high = in8(0x40);
64 	} while (high > 224);
65 	t2 = rdtsc();
66 
67 	p1 = t2-t1;
68 	r1 = (double)(p1) / (double)(((s_high << 8) | s_low) - ((high << 8) | low));
69 
70 	/* not so quick sample */
71 not_so_quick_sample:
72 	do {
73 		out8(0x00, 0x43); /* latch counter value */
74 		s_low = in8(0x40);
75 		s_high = in8(0x40);
76 	} while (s_high!= 255);
77 	t1 = rdtsc();
78 	do {
79 		out8(0x00, 0x43); /* latch counter value */
80 		low = in8(0x40);
81 		high = in8(0x40);
82 	} while (high> 192);
83 	t2 = rdtsc();
84 	p2 = t2-t1;
85 	r2 = (double)(p2) / (double)(((s_high << 8) | s_low) - ((high << 8) | low));
86 	if ((r1/r2) > 1.01) {
87 		//dprintf("Tuning loop(1)\n");
88 		goto quick_sample;
89 	}
90 	if ((r1/r2) < 0.99) {
91 		//dprintf("Tuning loop(1)\n");
92 		goto quick_sample;
93 	}
94 
95 	/* slow sample */
96 	do {
97 		out8(0x00, 0x43); /* latch counter value */
98 		s_low = in8(0x40);
99 		s_high = in8(0x40);
100 	} while (s_high!= 255);
101 	t1 = rdtsc();
102 	do {
103 		out8(0x00, 0x43); /* latch counter value */
104 		low = in8(0x40);
105 		high = in8(0x40);
106 	} while (high > 128);
107 	t2 = rdtsc();
108 
109 	p3 = t2-t1;
110 	r3 = (double)(p3) / (double)(((s_high << 8) | s_low) - ((high << 8) | low));
111 	if ((r2/r3) > 1.01) {
112 		TRACE(("Tuning loop(2)\n"));
113 		goto not_so_quick_sample;
114 	}
115 	if ((r2/r3) < 0.99) {
116 		TRACE(("Tuning loop(2)\n"));
117 		goto not_so_quick_sample;
118 	}
119 
120 	expired = ((s_high << 8) | s_low) - ((high << 8) | low);
121 	p3 *= TIMER_CLKNUM_HZ;
122 
123 	/*
124 	 * cv_factor contains time in usecs per CPU cycle * 2^32
125 	 *
126 	 * The code below is a bit fancy. Originally Michael Noistering
127 	 * had it like:
128 	 *
129 	 *     cv_factor = ((uint64)1000000<<32) * expired / p3;
130 	 *
131 	 * whic is perfect, but unfortunately 1000000ULL<<32*expired
132 	 * may overflow in fast cpus with the long sampling period
133 	 * i put there for being as accurate as possible under
134 	 * vmware.
135 	 *
136 	 * The below calculation is based in that we are trying
137 	 * to calculate:
138 	 *
139 	 *     (C*expired)/p3 -> (C*(x0<<k + x1))/p3 ->
140 	 *     (C*(x0<<k))/p3 + (C*x1)/p3
141 	 *
142 	 * Now the term (C*(x0<<k))/p3 is rewritten as:
143 	 *
144 	 *     (C*(x0<<k))/p3 -> ((C*x0)/p3)<<k + reminder
145 	 *
146 	 * where reminder is:
147 	 *
148 	 *     floor((1<<k)*decimalPart((C*x0)/p3))
149 	 *
150 	 * which is approximated as:
151 	 *
152 	 *     floor((1<<k)*decimalPart(((C*x0)%p3)/p3)) ->
153 	 *     (((C*x0)%p3)<<k)/p3
154 	 *
155 	 * So the final expression is:
156 	 *
157 	 *     ((C*x0)/p3)<<k + (((C*x0)%p3)<<k)/p3 + (C*x1)/p3
158 	 */
159 	 /*
160 	 * To get the highest accuracy with this method
161 	 * x0 should have the 12 most significant bits of expired
162 	 * to minimize the error upon <<k.
163 	 */
164 	 /*
165 	 * Of course, you are not expected to understand any of this.
166 	 */
167 	{
168 		unsigned i;
169 		unsigned k;
170 		uint64 C;
171 		uint64 x0;
172 		uint64 x1;
173 		uint64 a, b, c;
174 
175 		/* first calculate k*/
176 		k = 0;
177 		for (i = 12; i < 16; i++) {
178 			if (expired & (1<<i))
179 				k = i - 11;
180 		}
181 
182 		C = 1000000ULL << 32;
183 		x0 = expired >> k;
184 		x1 = expired & ((1 << k) - 1);
185 
186 		a = ((C * x0) / p3) << k;
187 		b = (((C * x0) % p3) << k) / p3;
188 		c = (C * x1) / p3;
189 #if 0
190 		dprintf("a=%Ld\n", a);
191 		dprintf("b=%Ld\n", b);
192 		dprintf("c=%Ld\n", c);
193 		dprintf("%d %Ld\n", expired, p3);
194 #endif
195 		gTimeConversionFactor = a + b + c;
196 #if 0
197 		dprintf("cvf=%Ld\n", cv_factor);
198 #endif
199 	}
200 
201 #ifdef TRACE_CPU
202 	if (p3 / expired / 1000000000LL)
203 		dprintf("CPU at %Ld.%03Ld GHz\n", p3/expired/1000000000LL, ((p3/expired)%1000000000LL)/1000000LL);
204 	else
205 		dprintf("CPU at %Ld.%03Ld MHz\n", p3/expired/1000000LL, ((p3/expired)%1000000LL)/1000LL);
206 #endif
207 
208 	gKernelArgs.arch_args.system_time_cv_factor = gTimeConversionFactor;
209 	gKernelArgs.arch_args.cpu_clock_speed = p3/expired;
210 }
211 
212 
213 static status_t
214 check_cpu_features()
215 {
216 	// ToDo: for now
217 	return B_OK;
218 }
219 
220 
221 //	#pragma mark -
222 
223 
224 extern "C" void
225 spin(bigtime_t microseconds)
226 {
227 	bigtime_t time = system_time();
228 
229 	while((system_time() - time) < microseconds)
230 		;
231 }
232 
233 
234 extern "C" void
235 cpu_init()
236 {
237 	if (check_cpu_features() != B_OK)
238 		panic("You need a Pentium or higher in order to boot!\n");
239 
240 	calculate_cpu_conversion_factor();
241 
242 	gKernelArgs.num_cpus = 1;
243 		// this will eventually be corrected later on
244 }
245 
246