Subversion Repositories shark

Rev

Rev 299 | Rev 302 | 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/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>
299 giacomo 32
#include <ll/i386/apic.h>
120 giacomo 33
#include <ll/sys/ll/event.h>
34
#include <ll/sys/ll/time.h>
35
 
264 giacomo 36
#define CALIBRATE_USING_CMOS
37
 
130 giacomo 38
unsigned char use_tsc = 1; //Enable the TSC counter mode
120 giacomo 39
unsigned char use_cmos = 0; //Enable the RTC correction
301 giacomo 40
unsigned char use_apic = 1; //Enable the APIC
120 giacomo 41
 
126 giacomo 42
//Max single delta_clk_per_msec increment = clk_per_msec / MAX_DIV_INK;
43
#define MAX_DIV_INK 30000
120 giacomo 44
 
45
signed long long init_tsc;
194 giacomo 46
signed long long * ptr_init_tsc = &init_tsc;
47
 
238 giacomo 48
signed long long init_nsec; //Wraparound 292 years
194 giacomo 49
signed long long * ptr_init_nsec = &init_nsec;
50
 
120 giacomo 51
signed long long clk_per_msec;
194 giacomo 52
signed long long * ptr_clk_per_msec = &clk_per_msec;
120 giacomo 53
 
299 giacomo 54
signed long long apic_clk_per_msec;
55
 
120 giacomo 56
signed long last_delta_clk_per_msec;
57
signed long total_delta_clk_per_msec;
58
 
59
unsigned char save_CMOS_regA;
60
unsigned char save_CMOS_regB;
61
 
242 giacomo 62
//#define IRQ8_DEBUG
63
 
120 giacomo 64
void HandlerIRQ8(void *p)
65
{
66
 
67
   unsigned char set;
194 giacomo 68
 
69
   static unsigned long Mconst = 1000000;
120 giacomo 70
 
71
   static unsigned long init_step = 0;
72
 
126 giacomo 73
   signed long max_dcms = clk_per_msec / MAX_DIV_INK;
120 giacomo 74
 
194 giacomo 75
   static signed long long dn;
76
   static signed long long * ptr_dn = &dn;
120 giacomo 77
   signed long delta_clk_per_msec;
126 giacomo 78
 
120 giacomo 79
   cli();
242 giacomo 80
 
81
   #ifdef IRQ8_DEBUG
82
     message("(IRQ8");
83
   #endif   
84
 
120 giacomo 85
   CMOS_READ(0x0C,set);
194 giacomo 86
 
242 giacomo 87
   __asm__("xorl %%eax,%%eax\n\t"
88
           "cpuid\n\t"
89
           "rdtsc\n\t"
194 giacomo 90
           "pushl %%eax\n\t"
91
           "pushl %%edx\n\t"
242 giacomo 92
           "pushl %%eax\n\t"
93
           "pushl %%edx\n\t"
94
           "xorl %%eax,%%eax\n\t"
95
           "cpuid\n\t"
96
           "popl %%edx\n\t"
97
           "popl %%eax\n\t"
194 giacomo 98
           "subl (%%edi),%%eax\n\t"
99
           "sbbl 4(%%edi),%%edx\n\t"
100
           "popl 4(%%edi)\n\t"
101
           "popl (%%edi)\n\t"
102
           "movl %%edx,%%ecx\n\t"
103
           "mull %4\n\t"
104
           "pushl %%eax\n\t"
105
           "movl %%ecx,%%eax\n\t"
106
           "movl %%edx,%%ecx\n\t"
107
           "mull %4\n\t"
108
           "addl %%ecx,%%eax\n\t"
109
           "adcl $0,%%edx\n\t"
242 giacomo 110
           "movl %7,%%ebx\n\t"
194 giacomo 111
           "divl (%%ebx)\n\t"
112
           "movl %%eax,4(%%esi)\n\t"
113
           "popl %%eax\n\t"
114
           "divl (%%ebx)\n\t"
115
           "movl %%eax,(%%esi)\n\t"
116
 
117
            :
242 giacomo 118
            : "D" (ptr_init_tsc), "S" (ptr_dn), "b" (0),
119
              "c" (0), "m" (Mconst), "a" (0), "d" (0), "m" (ptr_clk_per_msec));
120 giacomo 120
 
121
   //Offset
122
   init_nsec += dn;
194 giacomo 123
 
120 giacomo 124
   if (init_step < 5) {
125
           init_step++;
242 giacomo 126
           #ifdef IRQ8_DEBUG
127
             message(")");
128
           #endif
129
 
244 giacomo 130
           sti();
131
 
120 giacomo 132
           return;
133
   }
134
 
135
   dn = dn % 1000000000 - 500000000;
136
 
137
   //Delta clk/msec
138
   delta_clk_per_msec = dn * clk_per_msec / (500000000 - dn);
139
 
140
   //clk_per_msec adjustment
141
   if (delta_clk_per_msec < 0) {
142
 
126 giacomo 143
        if (delta_clk_per_msec > -max_dcms)
120 giacomo 144
                clk_per_msec += delta_clk_per_msec;
145
        else
126 giacomo 146
                clk_per_msec -= max_dcms;
120 giacomo 147
   } else {
148
 
126 giacomo 149
        if (delta_clk_per_msec < max_dcms)
120 giacomo 150
                clk_per_msec += delta_clk_per_msec;
151
        else
126 giacomo 152
                clk_per_msec += max_dcms;
120 giacomo 153
   }
154
 
155
   last_delta_clk_per_msec = delta_clk_per_msec;
156
   total_delta_clk_per_msec += delta_clk_per_msec;
157
 
242 giacomo 158
   #ifdef IRQ8_DEBUG
159
      message(")");
160
   #endif
161
 
120 giacomo 162
   sti();
163
 
164
}
165
 
166
#ifdef CONFIG_MELAN
167
#  define CLOCK_TICK_RATE 1189200 /* AMD Elan has different frequency! */
168
#else
249 giacomo 169
#  define CLOCK_TICK_RATE 1193182 /* Underlying HZ */
120 giacomo 170
#endif
171
 
248 giacomo 172
#define COUNTER_END 100
120 giacomo 173
 
245 giacomo 174
#define barrier() __asm__ __volatile__("" ::: "memory");
175
 
120 giacomo 176
//TSC Calibration (idea from the linux kernel code)
177
void ll_calibrate_tsc(void)
178
{
179
 
180
        signed long long start;
181
        signed long long end;
244 giacomo 182
        signed long long dtsc;
120 giacomo 183
 
244 giacomo 184
        signed long start_8253, end_8253, delta_8253;
120 giacomo 185
 
186
        cli();
187
 
248 giacomo 188
        outp(0x61, (inp(0x61) & ~0x02) | 0x01);
120 giacomo 189
 
190
        outp(0x43,0xB0);                        /* binary, mode 0, LSB/MSB, Ch 2 */
238 giacomo 191
        outp(0x42,0xFF);                        /* LSB of count */
192
        outp(0x42,0xFF);                        /* MSB of count */
242 giacomo 193
 
245 giacomo 194
        barrier();
195
        rdtscll(start);
196
        barrier();
243 giacomo 197
        outp(0x43,0x00);
198
        start_8253 = inp(0x42);
199
        start_8253 |= inp(0x42) << 8;
245 giacomo 200
        barrier();
264 giacomo 201
        rdtscll(start);
202
        barrier();
243 giacomo 203
 
204
        do {
205
 
206
            outp(0x43,0x00);
207
            end_8253 = inp(0x42);
208
            end_8253 |= inp(0x42) << 8;
209
 
210
        } while (end_8253 > COUNTER_END);
211
 
245 giacomo 212
        barrier();
213
        rdtscll(end);
214
        barrier();
243 giacomo 215
        outp(0x43,0x00);
216
        end_8253 = inp(0x42);
217
        end_8253 |= inp(0x42) << 8;
245 giacomo 218
        barrier();
264 giacomo 219
        rdtscll(end);
220
        barrier();
243 giacomo 221
 
222
        //Delta TSC
244 giacomo 223
        dtsc = end - start;
243 giacomo 224
 
225
        //Delta PIT
264 giacomo 226
        delta_8253 = start_8253 - end_8253;
243 giacomo 227
 
242 giacomo 228
        if (delta_8253 > 0x20000) {
120 giacomo 229
                message("Error calculating Delta PIT\n");
230
                ll_abort(10);
231
        }
232
 
233
        message("Delta TSC               = %10ld\n",(long)dtsc);
234
 
235
        message("Delta PIT               = %10ld\n",(long)delta_8253);
236
 
252 giacomo 237
        clk_per_msec = dtsc * CLOCK_TICK_RATE / delta_8253 / 1000;
120 giacomo 238
 
239
        message("Calibrated Clk_per_msec = %10ld\n",(long)clk_per_msec);
240
 
241
        sti();
242
 
243
}
244
 
264 giacomo 245
#define CMOS_INIT  0
246
#define CMOS_BEGIN 1
247
#define CMOS_START 2
248
#define CMOS_END   3
249
 
250
int cmos_calibrate_status = CMOS_INIT;
251
signed long long irq8_start;
252
signed long long irq8_end;
253
 
299 giacomo 254
void calibrate_tsc_IRQ8(void *p)
264 giacomo 255
{
256
 
257
  unsigned char set;
258
 
259
  cli();
260
 
261
  CMOS_READ(0x0C,set);
262
 
263
  barrier();
264
  rdtscll(irq8_end);
265
  barrier();
266
 
267
  if (cmos_calibrate_status == CMOS_START) {
268
    cmos_calibrate_status = CMOS_END;
269
  }
270
 
271
  if (cmos_calibrate_status == CMOS_BEGIN) {
272
    irq8_start = irq8_end;
273
    cmos_calibrate_status = CMOS_START;
274
  }
275
 
276
  if (cmos_calibrate_status == CMOS_INIT) {
277
    cmos_calibrate_status = CMOS_BEGIN;
278
  }
279
 
280
  sti();
281
 
282
}
283
 
284
//TSC Calibration using RTC
285
void ll_calibrate_tsc_cmos(void)
286
{
287
 
288
  signed long long dtsc;
289
 
290
  cli();           
291
 
299 giacomo 292
  irq_bind(8, calibrate_tsc_IRQ8, INT_FORCE);
264 giacomo 293
 
294
  CMOS_READ(0x0A,save_CMOS_regA);
295
  CMOS_READ(0x0B,save_CMOS_regB);
296
 
297
  CMOS_WRITE(0x0A,0x2F); // Set 2 Hz Periodic Interrupt
298
  CMOS_WRITE(0x0B,0x42); // Enable Interrupt
299
 
300
  irq_unmask(8);
301
 
302
  sti();
303
 
304
  while (cmos_calibrate_status != CMOS_END) {
305
    barrier();
306
  }
307
 
308
  dtsc = irq8_end - irq8_start;
309
 
310
  clk_per_msec = dtsc / 500;
311
 
299 giacomo 312
  message("Calibrated CPU Clk/msec  = %10ld\n",(long)clk_per_msec);
264 giacomo 313
 
314
  cli();
315
 
316
  irq_mask(8);
317
 
318
  CMOS_WRITE(0x0A,save_CMOS_regA);
319
  CMOS_WRITE(0x0B,save_CMOS_regB);
320
 
321
  sti();
322
 
323
}
324
 
120 giacomo 325
//Low level time read function
131 giacomo 326
void ll_read_timespec(struct timespec *tspec)
120 giacomo 327
{
328
 
194 giacomo 329
    static unsigned long Gconst = 1000000000;
330
    static unsigned long Mconst = 1000000;
120 giacomo 331
 
124 giacomo 332
    if (clk_per_msec <= 0) {
333
            NULL_TIMESPEC(tspec);
334
            return;
335
    }
336
 
242 giacomo 337
    __asm__("xorl %%eax,%%eax\n\t"
338
            "cpuid\n\t"  
339
            "rdtsc\n\t"
340
            "pushl %%eax\n\t"
341
            "pushl %%edx\n\t"
342
            "xorl %%eax,%%eax\n\t"
343
            "cpuid\n\t"
344
            "popl %%edx\n\t"
345
            "popl %%eax\n\t"
346
            "subl (%%edi),%%eax\n\t"
194 giacomo 347
            "sbbl 4(%%edi),%%edx\n\t"
348
            "movl %%edx,%%ecx\n\t"
349
            "mull %6\n\t"
350
            "pushl %%eax\n\t"
351
            "movl %%ecx,%%eax\n\t"
352
            "movl %%edx,%%ecx\n\t"
353
            "mull %6\n\t"
354
            "addl %%ecx,%%eax\n\t"
355
            "adcl $0,%%edx\n\t"
242 giacomo 356
            "movl %8,%%ebx\n\t"
194 giacomo 357
            "divl (%%ebx)\n\t"
358
            "movl %%eax,%%ecx\n\t"
359
            "popl %%eax\n\t"
360
            "divl (%%ebx)\n\t"
361
            "movl %%ecx,%%edx\n\t"
362
            "addl (%%esi),%%eax\n\t"
363
            "adcl 4(%%esi),%%edx\n\t"
364
            "divl %7\n\t"
365
 
366
            : "=a" (tspec->tv_sec), "=d" (tspec->tv_nsec)
242 giacomo 367
            : "D" (ptr_init_tsc), "S" (ptr_init_nsec), "b" (0),
368
              "c" (0), "m" (Mconst), "m" (Gconst), "m" (ptr_clk_per_msec));
194 giacomo 369
 
120 giacomo 370
}
371
 
299 giacomo 372
int apic_get_maxlvt(void)
373
{
374
        unsigned int v, ver, maxlvt;
375
 
376
        v = apic_read(APIC_LVR);
377
        ver = GET_APIC_VERSION(v);
378
        /* 82489DXs do not report # of LVT entries. */
379
        maxlvt = APIC_INTEGRATED(ver) ? GET_APIC_MAXLVT(v) : 2;
380
        return maxlvt;
381
}
382
 
383
void clear_local_APIC(void)
384
{
385
        int maxlvt;
386
        unsigned long v;
387
 
388
        maxlvt = apic_get_maxlvt();
389
 
390
        /*
391
         * Masking an LVT entry on a P6 can trigger a local APIC error
392
         * if the vector is zero. Mask LVTERR first to prevent this.
393
         */
394
        if (maxlvt >= 3) {
395
                v = 0xFF; /* any non-zero vector will do */
396
                apic_write_around(APIC_LVTERR, v | APIC_LVT_MASKED);
397
        }
398
        /*
399
         * Careful: we have to set masks only first to deassert
400
         * any level-triggered sources.
401
         */
402
        v = apic_read(APIC_LVTT);
403
        apic_write_around(APIC_LVTT, v | APIC_LVT_MASKED);
404
        v = apic_read(APIC_LVT0);
405
        apic_write_around(APIC_LVT0, v | APIC_LVT_MASKED);
406
        v = apic_read(APIC_LVT1);
407
        apic_write_around(APIC_LVT1, v | APIC_LVT_MASKED);
408
        if (maxlvt >= 4) {
409
                v = apic_read(APIC_LVTPC);
410
                apic_write_around(APIC_LVTPC, v | APIC_LVT_MASKED);
411
        }
412
 
413
        /*
414
         * Clean APIC state for other OSs:
415
         */
416
        apic_write_around(APIC_LVTT, APIC_LVT_MASKED);
417
        apic_write_around(APIC_LVT0, APIC_LVT_MASKED);
418
        apic_write_around(APIC_LVT1, APIC_LVT_MASKED);
419
        if (maxlvt >= 3)
420
                apic_write_around(APIC_LVTERR, APIC_LVT_MASKED);
421
        if (maxlvt >= 4)
422
                apic_write_around(APIC_LVTPC, APIC_LVT_MASKED);
423
        v = GET_APIC_VERSION(apic_read(APIC_LVR));
424
        if (APIC_INTEGRATED(v)) {       /* !82489DX */
425
                if (maxlvt > 3)
426
                        apic_write(APIC_ESR, 0);
427
                apic_read(APIC_ESR);
428
        }
429
}
430
 
431
void disable_local_APIC(void)
432
{
433
        unsigned long value;
434
 
435
        clear_local_APIC();
436
 
437
        /*
438
         * Disable APIC (implies clearing of registers
439
         * for 82489DX!).
440
         */
441
        value = apic_read(APIC_SPIV);
442
        value &= ~APIC_SPIV_APIC_ENABLED;
443
        apic_write_around(APIC_SPIV, value);
444
}
445
 
446
#define SPURIOUS_APIC_VECTOR 0xFF
447
 
448
/*
449
 * An initial setup of the virtual wire mode.
450
 */
451
 
452
void setup_local_APIC (void)
453
{
301 giacomo 454
        unsigned long value;
299 giacomo 455
 
456
        /* Pound the ESR really hard over the head with a big hammer - mbligh */
457
 
458
        apic_write(APIC_ESR, 0);
459
        apic_write(APIC_ESR, 0);
460
        apic_write(APIC_ESR, 0);
461
        apic_write(APIC_ESR, 0);
462
 
301 giacomo 463
        value = APIC_SPIV_FOCUS_DISABLED | APIC_SPIV_APIC_ENABLED | SPURIOUS_APIC_VECTOR;
464
        apic_write_around(APIC_SPIV, value);
299 giacomo 465
 
301 giacomo 466
        value = APIC_DM_EXTINT | APIC_LVT_LEVEL_TRIGGER;
467
        apic_write_around(APIC_LVT0, value);
299 giacomo 468
 
301 giacomo 469
        value = APIC_DM_NMI;
470
        apic_write_around(APIC_LVT1, value);
299 giacomo 471
 
301 giacomo 472
        apic_write(APIC_ESR, 0);
299 giacomo 473
 
474
}
475
 
476
void disable_APIC_timer(void)
477
{
478
        unsigned long v;
479
 
480
        v = apic_read(APIC_LVTT);
481
        apic_write_around(APIC_LVTT, v | APIC_LVT_MASKED);
482
 
483
}
484
 
485
void enable_APIC_timer(void)
486
{
487
        unsigned long v;
488
 
489
        v = apic_read(APIC_LVTT);
490
        apic_write_around(APIC_LVTT, v & ~APIC_LVT_MASKED);
491
 
492
}
493
 
301 giacomo 494
#define LOCAL_TIMER_VECTOR 0x66
299 giacomo 495
 
496
void setup_APIC_LVTT(unsigned int clocks)
497
{
498
        unsigned int lvtt1_value, tmp_value;
499
 
500
        lvtt1_value = SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV) |
501
                        APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR;
502
        apic_write_around(APIC_LVTT, lvtt1_value);
503
 
504
        /*
505
         * Divide PICLK by 1
506
         */
507
        tmp_value = apic_read(APIC_TDCR);
508
        apic_write_around(APIC_TDCR, (tmp_value
509
                                & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE))
510
                                | APIC_TDR_DIV_1);
511
 
512
        apic_write_around(APIC_TMICT, clocks);
513
}
514
 
515
#define APIC_LIMIT 0xFF000000
516
 
517
void ll_calibrate_apic(void)
518
{
519
 
520
  unsigned int apic_start = 0, apic_end = 0, dapic;
521
  signed long long tsc_start = 0, tsc_end = 0, dtsc;
522
  unsigned int tmp_value;
523
 
524
  cli();
525
 
526
  tmp_value = apic_read(APIC_TDCR);
527
  apic_write_around(APIC_TDCR, (tmp_value
528
                                 & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE))
529
                                 | APIC_TDR_DIV_1);
530
 
531
  apic_write(APIC_TMICT, MAX_DWORD);
532
 
533
  barrier();
534
  rdtscll(tsc_start);
535
  barrier();
536
  apic_start = apic_read(APIC_TMCCT);
537
  barrier();            
538
 
539
  while (apic_read(APIC_TMCCT) > APIC_LIMIT) {
540
    barrier();
541
    rdtscll(tsc_end);
542
  }
543
 
544
  barrier();
545
  rdtscll(tsc_end);
546
  barrier();
547
  apic_end = apic_read(APIC_TMCCT);
548
  barrier();    
549
 
550
  sti();
551
 
552
  dtsc = tsc_end - tsc_start;
553
  dapic = apic_start - apic_end;
554
 
555
  apic_clk_per_msec = clk_per_msec * (signed long long)(dapic) / dtsc;
556
 
557
  message("Calibrated APIC Clk/msec = %10ld\n",(long)apic_clk_per_msec);
558
 
559
}
560
 
120 giacomo 561
void ll_init_advtimer()
562
{
563
 
564
    if (use_tsc) {
565
 
264 giacomo 566
        #ifdef CALIBRATE_USING_CMOS
567
          ll_calibrate_tsc_cmos();
568
        #else
569
          ll_calibrate_tsc();
570
        #endif  
571
 
120 giacomo 572
        last_delta_clk_per_msec = 0;
573
        total_delta_clk_per_msec = 0;
574
 
575
        rdtscll(init_tsc); // Read start TSC
576
        init_nsec = 0;
577
 
299 giacomo 578
        if (use_apic) {
579
          unsigned long msr_low_orig, tmp;
580
 
581
          rdmsr(APIC_BASE_MSR, msr_low_orig, tmp);
582
          wrmsr(APIC_BASE_MSR, msr_low_orig|(1<<11), 0);
583
 
301 giacomo 584
          clear_local_APIC();
299 giacomo 585
 
301 giacomo 586
          ll_calibrate_apic();
587
 
299 giacomo 588
          setup_local_APIC();
589
 
590
        }
591
 
120 giacomo 592
        if (use_cmos) {
593
 
131 giacomo 594
            message("CMOS adjustement enabled\n");
120 giacomo 595
 
596
            cli();         
597
 
598
            irq_bind(8, HandlerIRQ8, INT_FORCE);
599
 
600
            CMOS_READ(0x0A,save_CMOS_regA);
601
            CMOS_READ(0x0B,save_CMOS_regB);
602
 
603
            CMOS_WRITE(0x0A,0x2F); // Set 2 Hz Periodic Interrupt
604
            CMOS_WRITE(0x0B,0x42); // Enable Interrupt
605
 
606
            irq_unmask(8);
607
 
608
            sti();
609
 
610
        }
611
 
612
    } else {
613
 
614
        use_cmos = 0;
615
 
616
   }
617
 
618
}
619
 
301 giacomo 620
void ll_restore_adv()
120 giacomo 621
{
622
        if (use_cmos) {
623
                cli();
624
 
625
                irq_mask(8);
626
 
627
                CMOS_WRITE(0x0A,save_CMOS_regA);
628
                CMOS_WRITE(0x0B,save_CMOS_regB);
629
 
630
                sti();         
631
        }
301 giacomo 632
 
633
        if (use_apic) {
634
                unsigned int msr_low_orig, tmp;
635
 
636
                cli();
637
 
638
                disable_APIC_timer();
639
 
640
                clear_local_APIC();
641
 
642
                rdmsr(APIC_BASE_MSR, msr_low_orig, tmp);
643
                wrmsr(APIC_BASE_MSR, msr_low_orig&~(1<<11), 0);
644
 
645
                sti();
646
 
647
        }
648
 
120 giacomo 649
}