Rev 305 | Rev 314 | Go to most recent revision | 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/sys/ll/ll-data.h> |
||
29 | #include <ll/sys/ll/ll-func.h> |
||
30 | #include <ll/i386/pic.h> |
||
299 | giacomo | 31 | #include <ll/i386/apic.h> |
305 | giacomo | 32 | #include <ll/i386/64bit.h> |
120 | giacomo | 33 | #include <ll/sys/ll/event.h> |
34 | #include <ll/sys/ll/time.h> |
||
305 | giacomo | 35 | #include <ll/i386/advtimer.h> |
120 | giacomo | 36 | |
264 | giacomo | 37 | #define CALIBRATE_USING_CMOS |
38 | |||
305 | giacomo | 39 | unsigned long long init_tsc; |
40 | unsigned long long * ptr_init_tsc = &init_tsc; |
||
120 | giacomo | 41 | |
305 | giacomo | 42 | unsigned long long init_nsec; //Wraparound 292 years |
43 | unsigned long long * ptr_init_nsec = &init_nsec; |
||
120 | giacomo | 44 | |
305 | giacomo | 45 | unsigned int clk_per_msec = 0; |
46 | unsigned int apic_clk_per_msec = 0; |
||
47 | unsigned int apic_set_limit = 0; |
||
194 | giacomo | 48 | |
120 | giacomo | 49 | unsigned char save_CMOS_regA; |
50 | unsigned char save_CMOS_regB; |
||
51 | |||
52 | #ifdef CONFIG_MELAN |
||
53 | # define CLOCK_TICK_RATE 1189200 /* AMD Elan has different frequency! */ |
||
54 | #else |
||
249 | giacomo | 55 | # define CLOCK_TICK_RATE 1193182 /* Underlying HZ */ |
120 | giacomo | 56 | #endif |
57 | |||
248 | giacomo | 58 | #define COUNTER_END 100 |
120 | giacomo | 59 | |
245 | giacomo | 60 | #define barrier() __asm__ __volatile__("" ::: "memory"); |
61 | |||
120 | giacomo | 62 | //TSC Calibration (idea from the linux kernel code) |
63 | void ll_calibrate_tsc(void) |
||
64 | { |
||
65 | |||
305 | giacomo | 66 | unsigned long long start; |
67 | unsigned long long end; |
||
68 | unsigned long long dtsc; |
||
120 | giacomo | 69 | |
305 | giacomo | 70 | unsigned int start_8253, end_8253, delta_8253; |
120 | giacomo | 71 | |
72 | cli(); |
||
73 | |||
248 | giacomo | 74 | outp(0x61, (inp(0x61) & ~0x02) | 0x01); |
120 | giacomo | 75 | |
76 | outp(0x43,0xB0); /* binary, mode 0, LSB/MSB, Ch 2 */ |
||
238 | giacomo | 77 | outp(0x42,0xFF); /* LSB of count */ |
78 | outp(0x42,0xFF); /* MSB of count */ |
||
242 | giacomo | 79 | |
245 | giacomo | 80 | barrier(); |
81 | rdtscll(start); |
||
82 | barrier(); |
||
243 | giacomo | 83 | outp(0x43,0x00); |
84 | start_8253 = inp(0x42); |
||
85 | start_8253 |= inp(0x42) << 8; |
||
245 | giacomo | 86 | barrier(); |
264 | giacomo | 87 | rdtscll(start); |
88 | barrier(); |
||
243 | giacomo | 89 | |
90 | do { |
||
91 | |||
92 | outp(0x43,0x00); |
||
93 | end_8253 = inp(0x42); |
||
94 | end_8253 |= inp(0x42) << 8; |
||
95 | |||
96 | } while (end_8253 > COUNTER_END); |
||
97 | |||
245 | giacomo | 98 | barrier(); |
99 | rdtscll(end); |
||
100 | barrier(); |
||
243 | giacomo | 101 | outp(0x43,0x00); |
102 | end_8253 = inp(0x42); |
||
103 | end_8253 |= inp(0x42) << 8; |
||
245 | giacomo | 104 | barrier(); |
264 | giacomo | 105 | rdtscll(end); |
106 | barrier(); |
||
243 | giacomo | 107 | |
108 | //Delta TSC |
||
244 | giacomo | 109 | dtsc = end - start; |
243 | giacomo | 110 | |
111 | //Delta PIT |
||
264 | giacomo | 112 | delta_8253 = start_8253 - end_8253; |
243 | giacomo | 113 | |
242 | giacomo | 114 | if (delta_8253 > 0x20000) { |
120 | giacomo | 115 | message("Error calculating Delta PIT\n"); |
116 | ll_abort(10); |
||
117 | } |
||
118 | |||
305 | giacomo | 119 | message("Delta TSC = %10d\n",(int)dtsc); |
120 | giacomo | 120 | |
305 | giacomo | 121 | message("Delta PIT = %10d\n",delta_8253); |
120 | giacomo | 122 | |
252 | giacomo | 123 | clk_per_msec = dtsc * CLOCK_TICK_RATE / delta_8253 / 1000; |
120 | giacomo | 124 | |
305 | giacomo | 125 | message("Calibrated Clk_per_msec = %10d\n",clk_per_msec); |
120 | giacomo | 126 | |
127 | sti(); |
||
128 | |||
129 | } |
||
130 | |||
264 | giacomo | 131 | #define CMOS_INIT 0 |
132 | #define CMOS_BEGIN 1 |
||
133 | #define CMOS_START 2 |
||
134 | #define CMOS_END 3 |
||
135 | |||
136 | int cmos_calibrate_status = CMOS_INIT; |
||
305 | giacomo | 137 | unsigned long long irq8_start; |
138 | unsigned long long irq8_end; |
||
264 | giacomo | 139 | |
299 | giacomo | 140 | void calibrate_tsc_IRQ8(void *p) |
264 | giacomo | 141 | { |
142 | |||
143 | unsigned char set; |
||
144 | |||
145 | cli(); |
||
146 | |||
147 | CMOS_READ(0x0C,set); |
||
148 | |||
149 | barrier(); |
||
150 | rdtscll(irq8_end); |
||
151 | barrier(); |
||
152 | |||
153 | if (cmos_calibrate_status == CMOS_START) { |
||
154 | cmos_calibrate_status = CMOS_END; |
||
155 | } |
||
156 | |||
157 | if (cmos_calibrate_status == CMOS_BEGIN) { |
||
158 | irq8_start = irq8_end; |
||
159 | cmos_calibrate_status = CMOS_START; |
||
160 | } |
||
161 | |||
162 | if (cmos_calibrate_status == CMOS_INIT) { |
||
163 | cmos_calibrate_status = CMOS_BEGIN; |
||
164 | } |
||
165 | |||
166 | sti(); |
||
167 | |||
168 | } |
||
169 | |||
170 | //TSC Calibration using RTC |
||
171 | void ll_calibrate_tsc_cmos(void) |
||
172 | { |
||
173 | |||
305 | giacomo | 174 | unsigned long long dtsc; |
264 | giacomo | 175 | |
176 | cli(); |
||
177 | |||
299 | giacomo | 178 | irq_bind(8, calibrate_tsc_IRQ8, INT_FORCE); |
264 | giacomo | 179 | |
180 | CMOS_READ(0x0A,save_CMOS_regA); |
||
181 | CMOS_READ(0x0B,save_CMOS_regB); |
||
182 | |||
183 | CMOS_WRITE(0x0A,0x2F); // Set 2 Hz Periodic Interrupt |
||
184 | CMOS_WRITE(0x0B,0x42); // Enable Interrupt |
||
185 | |||
186 | irq_unmask(8); |
||
187 | |||
188 | sti(); |
||
189 | |||
190 | while (cmos_calibrate_status != CMOS_END) { |
||
191 | barrier(); |
||
192 | } |
||
193 | |||
194 | dtsc = irq8_end - irq8_start; |
||
195 | |||
196 | clk_per_msec = dtsc / 500; |
||
197 | |||
305 | giacomo | 198 | message("Calibrated CPU Clk/msec = %10d\n",clk_per_msec); |
264 | giacomo | 199 | |
200 | cli(); |
||
201 | |||
202 | irq_mask(8); |
||
203 | |||
204 | CMOS_WRITE(0x0A,save_CMOS_regA); |
||
205 | CMOS_WRITE(0x0B,save_CMOS_regB); |
||
206 | |||
207 | sti(); |
||
208 | |||
209 | } |
||
210 | |||
299 | giacomo | 211 | int apic_get_maxlvt(void) |
212 | { |
||
213 | unsigned int v, ver, maxlvt; |
||
214 | |||
215 | v = apic_read(APIC_LVR); |
||
216 | ver = GET_APIC_VERSION(v); |
||
217 | /* 82489DXs do not report # of LVT entries. */ |
||
218 | maxlvt = APIC_INTEGRATED(ver) ? GET_APIC_MAXLVT(v) : 2; |
||
219 | return maxlvt; |
||
220 | } |
||
221 | |||
304 | giacomo | 222 | /* Clear local APIC, from Linux kernel */ |
299 | giacomo | 223 | void clear_local_APIC(void) |
224 | { |
||
225 | int maxlvt; |
||
226 | unsigned long v; |
||
227 | |||
228 | maxlvt = apic_get_maxlvt(); |
||
229 | |||
230 | /* |
||
231 | * Masking an LVT entry on a P6 can trigger a local APIC error |
||
232 | * if the vector is zero. Mask LVTERR first to prevent this. |
||
233 | */ |
||
234 | if (maxlvt >= 3) { |
||
235 | v = 0xFF; /* any non-zero vector will do */ |
||
236 | apic_write_around(APIC_LVTERR, v | APIC_LVT_MASKED); |
||
237 | } |
||
238 | /* |
||
239 | * Careful: we have to set masks only first to deassert |
||
240 | * any level-triggered sources. |
||
241 | */ |
||
242 | v = apic_read(APIC_LVTT); |
||
243 | apic_write_around(APIC_LVTT, v | APIC_LVT_MASKED); |
||
244 | v = apic_read(APIC_LVT0); |
||
245 | apic_write_around(APIC_LVT0, v | APIC_LVT_MASKED); |
||
246 | v = apic_read(APIC_LVT1); |
||
247 | apic_write_around(APIC_LVT1, v | APIC_LVT_MASKED); |
||
248 | if (maxlvt >= 4) { |
||
249 | v = apic_read(APIC_LVTPC); |
||
250 | apic_write_around(APIC_LVTPC, v | APIC_LVT_MASKED); |
||
251 | } |
||
252 | |||
253 | /* |
||
254 | * Clean APIC state for other OSs: |
||
255 | */ |
||
256 | apic_write_around(APIC_LVTT, APIC_LVT_MASKED); |
||
257 | apic_write_around(APIC_LVT0, APIC_LVT_MASKED); |
||
258 | apic_write_around(APIC_LVT1, APIC_LVT_MASKED); |
||
259 | if (maxlvt >= 3) |
||
260 | apic_write_around(APIC_LVTERR, APIC_LVT_MASKED); |
||
261 | if (maxlvt >= 4) |
||
262 | apic_write_around(APIC_LVTPC, APIC_LVT_MASKED); |
||
263 | v = GET_APIC_VERSION(apic_read(APIC_LVR)); |
||
264 | if (APIC_INTEGRATED(v)) { /* !82489DX */ |
||
265 | if (maxlvt > 3) |
||
266 | apic_write(APIC_ESR, 0); |
||
267 | apic_read(APIC_ESR); |
||
268 | } |
||
269 | } |
||
270 | |||
271 | void disable_local_APIC(void) |
||
272 | { |
||
273 | unsigned long value; |
||
274 | |||
275 | clear_local_APIC(); |
||
276 | |||
277 | /* |
||
278 | * Disable APIC (implies clearing of registers |
||
279 | * for 82489DX!). |
||
280 | */ |
||
281 | value = apic_read(APIC_SPIV); |
||
282 | value &= ~APIC_SPIV_APIC_ENABLED; |
||
283 | apic_write_around(APIC_SPIV, value); |
||
284 | } |
||
285 | |||
286 | #define SPURIOUS_APIC_VECTOR 0xFF |
||
287 | |||
288 | /* |
||
302 | giacomo | 289 | * Setup the local APIC, minimal code to run P6 APIC |
299 | giacomo | 290 | */ |
291 | void setup_local_APIC (void) |
||
292 | { |
||
301 | giacomo | 293 | unsigned long value; |
299 | giacomo | 294 | |
295 | /* Pound the ESR really hard over the head with a big hammer - mbligh */ |
||
296 | |||
297 | apic_write(APIC_ESR, 0); |
||
298 | apic_write(APIC_ESR, 0); |
||
299 | apic_write(APIC_ESR, 0); |
||
300 | apic_write(APIC_ESR, 0); |
||
301 | |||
301 | giacomo | 302 | value = APIC_SPIV_FOCUS_DISABLED | APIC_SPIV_APIC_ENABLED | SPURIOUS_APIC_VECTOR; |
303 | apic_write_around(APIC_SPIV, value); |
||
299 | giacomo | 304 | |
301 | giacomo | 305 | value = APIC_DM_EXTINT | APIC_LVT_LEVEL_TRIGGER; |
306 | apic_write_around(APIC_LVT0, value); |
||
299 | giacomo | 307 | |
301 | giacomo | 308 | value = APIC_DM_NMI; |
309 | apic_write_around(APIC_LVT1, value); |
||
299 | giacomo | 310 | |
301 | giacomo | 311 | apic_write(APIC_ESR, 0); |
299 | giacomo | 312 | |
313 | } |
||
314 | |||
315 | void disable_APIC_timer(void) |
||
316 | { |
||
317 | unsigned long v; |
||
318 | |||
319 | v = apic_read(APIC_LVTT); |
||
320 | apic_write_around(APIC_LVTT, v | APIC_LVT_MASKED); |
||
321 | |||
322 | } |
||
323 | |||
324 | void enable_APIC_timer(void) |
||
325 | { |
||
326 | unsigned long v; |
||
327 | |||
328 | v = apic_read(APIC_LVTT); |
||
329 | apic_write_around(APIC_LVTT, v & ~APIC_LVT_MASKED); |
||
330 | |||
331 | } |
||
332 | |||
304 | giacomo | 333 | #define LOCAL_TIMER_VECTOR 0x39 |
302 | giacomo | 334 | |
303 | giacomo | 335 | /* Set APIC Timer... from Linux kernel */ |
336 | void setup_APIC_timer() |
||
299 | giacomo | 337 | { |
338 | unsigned int lvtt1_value, tmp_value; |
||
339 | |||
340 | lvtt1_value = SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV) | |
||
341 | APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR; |
||
342 | apic_write_around(APIC_LVTT, lvtt1_value); |
||
343 | |||
344 | /* |
||
345 | * Divide PICLK by 1 |
||
346 | */ |
||
312 | giacomo | 347 | apic_write_around(APIC_TDCR, APIC_TDR_DIV_1); |
303 | giacomo | 348 | |
349 | apic_write_around(APIC_TMICT, 0xFFFFFFFF); |
||
350 | |||
351 | disable_APIC_timer(); |
||
299 | giacomo | 352 | } |
353 | |||
354 | #define APIC_LIMIT 0xFF000000 |
||
303 | giacomo | 355 | #define APIC_SET_LIMIT 10 |
299 | giacomo | 356 | |
357 | void ll_calibrate_apic(void) |
||
358 | { |
||
359 | |||
360 | unsigned int apic_start = 0, apic_end = 0, dapic; |
||
305 | giacomo | 361 | unsigned long long tsc_start = 0, tsc_end = 0, dtsc; |
299 | giacomo | 362 | unsigned int tmp_value; |
363 | |||
364 | cli(); |
||
365 | |||
312 | giacomo | 366 | tmp_value = SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV) | LOCAL_TIMER_VECTOR; |
367 | apic_write_around(APIC_LVTT, tmp_value); |
||
299 | giacomo | 368 | |
312 | giacomo | 369 | apic_write_around(APIC_TDCR, APIC_TDR_DIV_1); |
370 | |||
299 | giacomo | 371 | apic_write(APIC_TMICT, MAX_DWORD); |
372 | |||
312 | giacomo | 373 | enable_APIC_timer(); |
374 | |||
299 | giacomo | 375 | barrier(); |
376 | rdtscll(tsc_start); |
||
377 | barrier(); |
||
378 | apic_start = apic_read(APIC_TMCCT); |
||
379 | barrier(); |
||
380 | |||
381 | while (apic_read(APIC_TMCCT) > APIC_LIMIT) { |
||
382 | barrier(); |
||
383 | rdtscll(tsc_end); |
||
384 | } |
||
385 | |||
386 | barrier(); |
||
387 | rdtscll(tsc_end); |
||
388 | barrier(); |
||
389 | apic_end = apic_read(APIC_TMCCT); |
||
390 | barrier(); |
||
391 | |||
312 | giacomo | 392 | disable_APIC_timer(); |
393 | |||
299 | giacomo | 394 | sti(); |
395 | |||
396 | dtsc = tsc_end - tsc_start; |
||
397 | dapic = apic_start - apic_end; |
||
398 | |||
305 | giacomo | 399 | apic_clk_per_msec = (unsigned long long)(clk_per_msec) * (unsigned long long)(dapic) / dtsc; |
312 | giacomo | 400 | apic_set_limit = ((apic_clk_per_msec / 100) != 0) ? (apic_clk_per_msec/100) : APIC_SET_LIMIT; |
303 | giacomo | 401 | |
305 | giacomo | 402 | message("Calibrated APIC Clk/msec = %10d\n",apic_clk_per_msec); |
299 | giacomo | 403 | |
404 | } |
||
405 | |||
120 | giacomo | 406 | void ll_init_advtimer() |
407 | { |
||
305 | giacomo | 408 | #ifdef __APIC__ |
409 | unsigned long msr_low_orig, tmp; |
||
410 | #endif |
||
120 | giacomo | 411 | |
305 | giacomo | 412 | #ifdef __TSC__ |
120 | giacomo | 413 | |
264 | giacomo | 414 | #ifdef CALIBRATE_USING_CMOS |
415 | ll_calibrate_tsc_cmos(); |
||
416 | #else |
||
417 | ll_calibrate_tsc(); |
||
418 | #endif |
||
419 | |||
120 | giacomo | 420 | rdtscll(init_tsc); // Read start TSC |
421 | init_nsec = 0; |
||
422 | |||
305 | giacomo | 423 | #ifdef __APIC__ |
299 | giacomo | 424 | |
425 | rdmsr(APIC_BASE_MSR, msr_low_orig, tmp); |
||
426 | wrmsr(APIC_BASE_MSR, msr_low_orig|(1<<11), 0); |
||
427 | |||
301 | giacomo | 428 | clear_local_APIC(); |
299 | giacomo | 429 | |
301 | giacomo | 430 | ll_calibrate_apic(); |
431 | |||
299 | giacomo | 432 | setup_local_APIC(); |
303 | giacomo | 433 | |
434 | setup_APIC_timer(); |
||
299 | giacomo | 435 | |
305 | giacomo | 436 | #endif |
299 | giacomo | 437 | |
305 | giacomo | 438 | #endif |
120 | giacomo | 439 | |
440 | } |
||
441 | |||
301 | giacomo | 442 | void ll_restore_adv() |
120 | giacomo | 443 | { |
302 | giacomo | 444 | /* Disable APIC */ |
305 | giacomo | 445 | #ifdef __APIC__ |
446 | unsigned int msr_low_orig, tmp; |
||
301 | giacomo | 447 | |
448 | cli(); |
||
449 | |||
450 | disable_APIC_timer(); |
||
451 | |||
452 | rdmsr(APIC_BASE_MSR, msr_low_orig, tmp); |
||
453 | wrmsr(APIC_BASE_MSR, msr_low_orig&~(1<<11), 0); |
||
454 | |||
455 | sti(); |
||
456 | |||
305 | giacomo | 457 | #endif |
301 | giacomo | 458 | |
120 | giacomo | 459 | } |