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