Subversion Repositories shark

Rev

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