Subversion Repositories shark

Rev

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