Rev 255 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
120 | giacomo | 1 | /* Project: OSLib |
2 | * Description: The OS Construction Kit |
||
3 | * Date: 1.6.2000 |
||
4 | * Idea by: Luca Abeni & Gerardo Lamastra |
||
5 | * |
||
6 | * OSLib is an SO project aimed at developing a common, easy-to-use |
||
7 | * low-level infrastructure for developing OS kernels and Embedded |
||
8 | * Applications; it partially derives from the HARTIK project but it |
||
9 | * currently is independently developed. |
||
10 | * |
||
11 | * OSLib is distributed under GPL License, and some of its code has |
||
12 | * been derived from the Linux kernel source; also some important |
||
13 | * ideas come from studying the DJGPP go32 extender. |
||
14 | * |
||
15 | * We acknowledge the Linux Community, Free Software Foundation, |
||
16 | * D.J. Delorie and all the other developers who believe in the |
||
17 | * freedom of software and ideas. |
||
18 | * |
||
19 | * For legalese, check out the included GPL license. |
||
20 | */ |
||
21 | |||
22 | /* Advanced Timer Managment |
||
23 | * Author: Giacomo Guidi <giacomo@gandalf.sssup.it> |
||
24 | */ |
||
25 | |||
26 | #include <ll/i386/stdlib.h> |
||
27 | #include <ll/i386/error.h> |
||
28 | #include <ll/i386/advtimer.h> |
||
29 | #include <ll/sys/ll/ll-data.h> |
||
30 | #include <ll/sys/ll/ll-func.h> |
||
31 | #include <ll/i386/pic.h> |
||
32 | #include <ll/sys/ll/event.h> |
||
33 | #include <ll/sys/ll/time.h> |
||
34 | |||
264 | giacomo | 35 | #define CALIBRATE_USING_CMOS |
36 | |||
130 | giacomo | 37 | unsigned char use_tsc = 1; //Enable the TSC counter mode |
120 | giacomo | 38 | unsigned char use_cmos = 0; //Enable the RTC correction |
39 | |||
126 | giacomo | 40 | //Max single delta_clk_per_msec increment = clk_per_msec / MAX_DIV_INK; |
41 | #define MAX_DIV_INK 30000 |
||
120 | giacomo | 42 | |
43 | signed long long init_tsc; |
||
194 | giacomo | 44 | signed long long * ptr_init_tsc = &init_tsc; |
45 | |||
238 | giacomo | 46 | signed long long init_nsec; //Wraparound 292 years |
194 | giacomo | 47 | signed long long * ptr_init_nsec = &init_nsec; |
48 | |||
120 | giacomo | 49 | signed long long clk_per_msec; |
194 | giacomo | 50 | signed long long * ptr_clk_per_msec = &clk_per_msec; |
120 | giacomo | 51 | |
52 | signed long last_delta_clk_per_msec; |
||
53 | signed long total_delta_clk_per_msec; |
||
54 | |||
55 | unsigned char save_CMOS_regA; |
||
56 | unsigned char save_CMOS_regB; |
||
57 | |||
242 | giacomo | 58 | //#define IRQ8_DEBUG |
59 | |||
120 | giacomo | 60 | void HandlerIRQ8(void *p) |
61 | { |
||
62 | |||
63 | unsigned char set; |
||
194 | giacomo | 64 | |
65 | static unsigned long Mconst = 1000000; |
||
120 | giacomo | 66 | |
67 | static unsigned long init_step = 0; |
||
68 | |||
126 | giacomo | 69 | signed long max_dcms = clk_per_msec / MAX_DIV_INK; |
120 | giacomo | 70 | |
194 | giacomo | 71 | static signed long long dn; |
72 | static signed long long * ptr_dn = &dn; |
||
120 | giacomo | 73 | signed long delta_clk_per_msec; |
126 | giacomo | 74 | |
120 | giacomo | 75 | cli(); |
242 | giacomo | 76 | |
77 | #ifdef IRQ8_DEBUG |
||
78 | message("(IRQ8"); |
||
79 | #endif |
||
80 | |||
120 | giacomo | 81 | CMOS_READ(0x0C,set); |
194 | giacomo | 82 | |
242 | giacomo | 83 | __asm__("xorl %%eax,%%eax\n\t" |
84 | "cpuid\n\t" |
||
85 | "rdtsc\n\t" |
||
194 | giacomo | 86 | "pushl %%eax\n\t" |
87 | "pushl %%edx\n\t" |
||
242 | giacomo | 88 | "pushl %%eax\n\t" |
89 | "pushl %%edx\n\t" |
||
90 | "xorl %%eax,%%eax\n\t" |
||
91 | "cpuid\n\t" |
||
92 | "popl %%edx\n\t" |
||
93 | "popl %%eax\n\t" |
||
194 | giacomo | 94 | "subl (%%edi),%%eax\n\t" |
95 | "sbbl 4(%%edi),%%edx\n\t" |
||
96 | "popl 4(%%edi)\n\t" |
||
97 | "popl (%%edi)\n\t" |
||
98 | "movl %%edx,%%ecx\n\t" |
||
99 | "mull %4\n\t" |
||
100 | "pushl %%eax\n\t" |
||
101 | "movl %%ecx,%%eax\n\t" |
||
102 | "movl %%edx,%%ecx\n\t" |
||
103 | "mull %4\n\t" |
||
104 | "addl %%ecx,%%eax\n\t" |
||
105 | "adcl $0,%%edx\n\t" |
||
242 | giacomo | 106 | "movl %7,%%ebx\n\t" |
194 | giacomo | 107 | "divl (%%ebx)\n\t" |
108 | "movl %%eax,4(%%esi)\n\t" |
||
109 | "popl %%eax\n\t" |
||
110 | "divl (%%ebx)\n\t" |
||
111 | "movl %%eax,(%%esi)\n\t" |
||
112 | |||
113 | : |
||
242 | giacomo | 114 | : "D" (ptr_init_tsc), "S" (ptr_dn), "b" (0), |
115 | "c" (0), "m" (Mconst), "a" (0), "d" (0), "m" (ptr_clk_per_msec)); |
||
120 | giacomo | 116 | |
117 | //Offset |
||
118 | init_nsec += dn; |
||
194 | giacomo | 119 | |
120 | giacomo | 120 | if (init_step < 5) { |
121 | init_step++; |
||
242 | giacomo | 122 | #ifdef IRQ8_DEBUG |
123 | message(")"); |
||
124 | #endif |
||
125 | |||
244 | giacomo | 126 | sti(); |
127 | |||
120 | giacomo | 128 | return; |
129 | } |
||
130 | |||
131 | dn = dn % 1000000000 - 500000000; |
||
132 | |||
133 | //Delta clk/msec |
||
134 | delta_clk_per_msec = dn * clk_per_msec / (500000000 - dn); |
||
135 | |||
136 | //clk_per_msec adjustment |
||
137 | if (delta_clk_per_msec < 0) { |
||
138 | |||
126 | giacomo | 139 | if (delta_clk_per_msec > -max_dcms) |
120 | giacomo | 140 | clk_per_msec += delta_clk_per_msec; |
141 | else |
||
126 | giacomo | 142 | clk_per_msec -= max_dcms; |
120 | giacomo | 143 | } else { |
144 | |||
126 | giacomo | 145 | if (delta_clk_per_msec < max_dcms) |
120 | giacomo | 146 | clk_per_msec += delta_clk_per_msec; |
147 | else |
||
126 | giacomo | 148 | clk_per_msec += max_dcms; |
120 | giacomo | 149 | } |
150 | |||
151 | last_delta_clk_per_msec = delta_clk_per_msec; |
||
152 | total_delta_clk_per_msec += delta_clk_per_msec; |
||
153 | |||
242 | giacomo | 154 | #ifdef IRQ8_DEBUG |
155 | message(")"); |
||
156 | #endif |
||
157 | |||
120 | giacomo | 158 | sti(); |
159 | |||
160 | } |
||
161 | |||
162 | #ifdef CONFIG_MELAN |
||
163 | # define CLOCK_TICK_RATE 1189200 /* AMD Elan has different frequency! */ |
||
164 | #else |
||
249 | giacomo | 165 | # define CLOCK_TICK_RATE 1193182 /* Underlying HZ */ |
120 | giacomo | 166 | #endif |
167 | |||
248 | giacomo | 168 | #define COUNTER_END 100 |
120 | giacomo | 169 | |
245 | giacomo | 170 | #define barrier() __asm__ __volatile__("" ::: "memory"); |
171 | |||
120 | giacomo | 172 | //TSC Calibration (idea from the linux kernel code) |
173 | void ll_calibrate_tsc(void) |
||
174 | { |
||
175 | |||
176 | signed long long start; |
||
177 | signed long long end; |
||
244 | giacomo | 178 | signed long long dtsc; |
120 | giacomo | 179 | |
244 | giacomo | 180 | signed long start_8253, end_8253, delta_8253; |
120 | giacomo | 181 | |
182 | cli(); |
||
183 | |||
248 | giacomo | 184 | outp(0x61, (inp(0x61) & ~0x02) | 0x01); |
120 | giacomo | 185 | |
186 | outp(0x43,0xB0); /* binary, mode 0, LSB/MSB, Ch 2 */ |
||
238 | giacomo | 187 | outp(0x42,0xFF); /* LSB of count */ |
188 | outp(0x42,0xFF); /* MSB of count */ |
||
242 | giacomo | 189 | |
245 | giacomo | 190 | barrier(); |
191 | rdtscll(start); |
||
192 | barrier(); |
||
243 | giacomo | 193 | outp(0x43,0x00); |
194 | start_8253 = inp(0x42); |
||
195 | start_8253 |= inp(0x42) << 8; |
||
245 | giacomo | 196 | barrier(); |
264 | giacomo | 197 | rdtscll(start); |
198 | barrier(); |
||
243 | giacomo | 199 | |
200 | do { |
||
201 | |||
202 | outp(0x43,0x00); |
||
203 | end_8253 = inp(0x42); |
||
204 | end_8253 |= inp(0x42) << 8; |
||
205 | |||
206 | } while (end_8253 > COUNTER_END); |
||
207 | |||
245 | giacomo | 208 | barrier(); |
209 | rdtscll(end); |
||
210 | barrier(); |
||
243 | giacomo | 211 | outp(0x43,0x00); |
212 | end_8253 = inp(0x42); |
||
213 | end_8253 |= inp(0x42) << 8; |
||
245 | giacomo | 214 | barrier(); |
264 | giacomo | 215 | rdtscll(end); |
216 | barrier(); |
||
243 | giacomo | 217 | |
218 | //Delta TSC |
||
244 | giacomo | 219 | dtsc = end - start; |
243 | giacomo | 220 | |
221 | //Delta PIT |
||
264 | giacomo | 222 | delta_8253 = start_8253 - end_8253; |
243 | giacomo | 223 | |
242 | giacomo | 224 | if (delta_8253 > 0x20000) { |
120 | giacomo | 225 | message("Error calculating Delta PIT\n"); |
226 | ll_abort(10); |
||
227 | } |
||
228 | |||
229 | message("Delta TSC = %10ld\n",(long)dtsc); |
||
230 | |||
231 | message("Delta PIT = %10ld\n",(long)delta_8253); |
||
232 | |||
252 | giacomo | 233 | clk_per_msec = dtsc * CLOCK_TICK_RATE / delta_8253 / 1000; |
120 | giacomo | 234 | |
235 | message("Calibrated Clk_per_msec = %10ld\n",(long)clk_per_msec); |
||
236 | |||
237 | sti(); |
||
238 | |||
239 | } |
||
240 | |||
264 | giacomo | 241 | #define CMOS_INIT 0 |
242 | #define CMOS_BEGIN 1 |
||
243 | #define CMOS_START 2 |
||
244 | #define CMOS_END 3 |
||
245 | |||
246 | int cmos_calibrate_status = CMOS_INIT; |
||
247 | signed long long irq8_start; |
||
248 | signed long long irq8_end; |
||
249 | |||
250 | void calibrate_IRQ8(void *p) |
||
251 | { |
||
252 | |||
253 | unsigned char set; |
||
254 | |||
255 | cli(); |
||
256 | |||
257 | CMOS_READ(0x0C,set); |
||
258 | |||
259 | barrier(); |
||
260 | rdtscll(irq8_end); |
||
261 | barrier(); |
||
262 | |||
263 | if (cmos_calibrate_status == CMOS_START) { |
||
264 | cmos_calibrate_status = CMOS_END; |
||
265 | } |
||
266 | |||
267 | if (cmos_calibrate_status == CMOS_BEGIN) { |
||
268 | irq8_start = irq8_end; |
||
269 | cmos_calibrate_status = CMOS_START; |
||
270 | } |
||
271 | |||
272 | if (cmos_calibrate_status == CMOS_INIT) { |
||
273 | cmos_calibrate_status = CMOS_BEGIN; |
||
274 | } |
||
275 | |||
276 | sti(); |
||
277 | |||
278 | } |
||
279 | |||
280 | //TSC Calibration using RTC |
||
281 | void ll_calibrate_tsc_cmos(void) |
||
282 | { |
||
283 | |||
284 | signed long long dtsc; |
||
285 | |||
286 | cli(); |
||
287 | |||
288 | irq_bind(8, calibrate_IRQ8, INT_FORCE); |
||
289 | |||
290 | CMOS_READ(0x0A,save_CMOS_regA); |
||
291 | CMOS_READ(0x0B,save_CMOS_regB); |
||
292 | |||
293 | CMOS_WRITE(0x0A,0x2F); // Set 2 Hz Periodic Interrupt |
||
294 | CMOS_WRITE(0x0B,0x42); // Enable Interrupt |
||
295 | |||
296 | irq_unmask(8); |
||
297 | |||
298 | sti(); |
||
299 | |||
300 | while (cmos_calibrate_status != CMOS_END) { |
||
301 | barrier(); |
||
302 | } |
||
303 | |||
304 | dtsc = irq8_end - irq8_start; |
||
305 | |||
306 | clk_per_msec = dtsc / 500; |
||
307 | |||
308 | message("Calibrated Clk_per_msec = %10ld\n",(long)clk_per_msec); |
||
309 | |||
310 | cli(); |
||
311 | |||
312 | irq_mask(8); |
||
313 | |||
314 | CMOS_WRITE(0x0A,save_CMOS_regA); |
||
315 | CMOS_WRITE(0x0B,save_CMOS_regB); |
||
316 | |||
317 | sti(); |
||
318 | |||
319 | } |
||
320 | |||
120 | giacomo | 321 | //Low level time read function |
131 | giacomo | 322 | void ll_read_timespec(struct timespec *tspec) |
120 | giacomo | 323 | { |
324 | |||
194 | giacomo | 325 | static unsigned long Gconst = 1000000000; |
326 | static unsigned long Mconst = 1000000; |
||
120 | giacomo | 327 | |
124 | giacomo | 328 | if (clk_per_msec <= 0) { |
329 | NULL_TIMESPEC(tspec); |
||
330 | return; |
||
331 | } |
||
332 | |||
242 | giacomo | 333 | __asm__("xorl %%eax,%%eax\n\t" |
334 | "cpuid\n\t" |
||
335 | "rdtsc\n\t" |
||
336 | "pushl %%eax\n\t" |
||
337 | "pushl %%edx\n\t" |
||
338 | "xorl %%eax,%%eax\n\t" |
||
339 | "cpuid\n\t" |
||
340 | "popl %%edx\n\t" |
||
341 | "popl %%eax\n\t" |
||
342 | "subl (%%edi),%%eax\n\t" |
||
194 | giacomo | 343 | "sbbl 4(%%edi),%%edx\n\t" |
344 | "movl %%edx,%%ecx\n\t" |
||
345 | "mull %6\n\t" |
||
346 | "pushl %%eax\n\t" |
||
347 | "movl %%ecx,%%eax\n\t" |
||
348 | "movl %%edx,%%ecx\n\t" |
||
349 | "mull %6\n\t" |
||
350 | "addl %%ecx,%%eax\n\t" |
||
351 | "adcl $0,%%edx\n\t" |
||
242 | giacomo | 352 | "movl %8,%%ebx\n\t" |
194 | giacomo | 353 | "divl (%%ebx)\n\t" |
354 | "movl %%eax,%%ecx\n\t" |
||
355 | "popl %%eax\n\t" |
||
356 | "divl (%%ebx)\n\t" |
||
357 | "movl %%ecx,%%edx\n\t" |
||
358 | "addl (%%esi),%%eax\n\t" |
||
359 | "adcl 4(%%esi),%%edx\n\t" |
||
360 | "divl %7\n\t" |
||
361 | |||
362 | : "=a" (tspec->tv_sec), "=d" (tspec->tv_nsec) |
||
242 | giacomo | 363 | : "D" (ptr_init_tsc), "S" (ptr_init_nsec), "b" (0), |
364 | "c" (0), "m" (Mconst), "m" (Gconst), "m" (ptr_clk_per_msec)); |
||
194 | giacomo | 365 | |
120 | giacomo | 366 | } |
367 | |||
368 | void ll_init_advtimer() |
||
369 | { |
||
370 | |||
371 | if (use_tsc) { |
||
372 | |||
264 | giacomo | 373 | #ifdef CALIBRATE_USING_CMOS |
374 | ll_calibrate_tsc_cmos(); |
||
375 | #else |
||
376 | ll_calibrate_tsc(); |
||
377 | #endif |
||
378 | |||
120 | giacomo | 379 | last_delta_clk_per_msec = 0; |
380 | total_delta_clk_per_msec = 0; |
||
381 | |||
382 | rdtscll(init_tsc); // Read start TSC |
||
383 | init_nsec = 0; |
||
384 | |||
385 | if (use_cmos) { |
||
386 | |||
131 | giacomo | 387 | message("CMOS adjustement enabled\n"); |
120 | giacomo | 388 | |
389 | cli(); |
||
390 | |||
391 | irq_bind(8, HandlerIRQ8, INT_FORCE); |
||
392 | |||
393 | CMOS_READ(0x0A,save_CMOS_regA); |
||
394 | CMOS_READ(0x0B,save_CMOS_regB); |
||
395 | |||
396 | CMOS_WRITE(0x0A,0x2F); // Set 2 Hz Periodic Interrupt |
||
397 | CMOS_WRITE(0x0B,0x42); // Enable Interrupt |
||
398 | |||
399 | irq_unmask(8); |
||
400 | |||
401 | sti(); |
||
402 | |||
403 | } |
||
404 | |||
405 | } else { |
||
406 | |||
407 | use_cmos = 0; |
||
408 | |||
409 | } |
||
410 | |||
411 | } |
||
412 | |||
131 | giacomo | 413 | void ll_restore_CMOS() |
120 | giacomo | 414 | { |
415 | if (use_cmos) { |
||
416 | cli(); |
||
417 | |||
418 | irq_mask(8); |
||
419 | |||
420 | CMOS_WRITE(0x0A,save_CMOS_regA); |
||
421 | CMOS_WRITE(0x0B,save_CMOS_regB); |
||
422 | |||
423 | sti(); |
||
424 | } |
||
425 | } |