Subversion Repositories shark

Rev

Rev 304 | Rev 312 | 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
         */
347
        tmp_value = apic_read(APIC_TDCR);
348
        apic_write_around(APIC_TDCR, (tmp_value
349
                                & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE))
350
                                | APIC_TDR_DIV_1);
303 giacomo 351
 
352
        apic_write_around(APIC_TMICT, 0xFFFFFFFF);
353
 
354
        disable_APIC_timer();                                                                                                                            
299 giacomo 355
}
356
 
357
#define APIC_LIMIT 0xFF000000
303 giacomo 358
#define APIC_SET_LIMIT 10
299 giacomo 359
 
360
void ll_calibrate_apic(void)
361
{
362
 
363
  unsigned int apic_start = 0, apic_end = 0, dapic;
305 giacomo 364
  unsigned long long tsc_start = 0, tsc_end = 0, dtsc;
299 giacomo 365
  unsigned int tmp_value;
366
 
367
  cli();
368
 
369
  tmp_value = apic_read(APIC_TDCR);
370
  apic_write_around(APIC_TDCR, (tmp_value
371
                                 & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE))
372
                                 | APIC_TDR_DIV_1);
373
 
374
  apic_write(APIC_TMICT, MAX_DWORD);
375
 
376
  barrier();
377
  rdtscll(tsc_start);
378
  barrier();
379
  apic_start = apic_read(APIC_TMCCT);
380
  barrier();            
381
 
382
  while (apic_read(APIC_TMCCT) > APIC_LIMIT) {
383
    barrier();
384
    rdtscll(tsc_end);
385
  }
386
 
387
  barrier();
388
  rdtscll(tsc_end);
389
  barrier();
390
  apic_end = apic_read(APIC_TMCCT);
391
  barrier();    
392
 
393
  sti();
394
 
395
  dtsc = tsc_end - tsc_start;
396
  dapic = apic_start - apic_end;
397
 
305 giacomo 398
  apic_clk_per_msec = (unsigned long long)(clk_per_msec) * (unsigned long long)(dapic) / dtsc;
303 giacomo 399
  apic_set_limit = ((apic_clk_per_msec / 100) == 0) ? (apic_clk_per_msec/100) : APIC_SET_LIMIT;  
400
 
305 giacomo 401
  message("Calibrated APIC Clk/msec = %10d\n",apic_clk_per_msec);
299 giacomo 402
 
403
}
404
 
120 giacomo 405
void ll_init_advtimer()
406
{
305 giacomo 407
    #ifdef __APIC__
408
      unsigned long msr_low_orig, tmp;
409
    #endif
120 giacomo 410
 
305 giacomo 411
    #ifdef __TSC__
120 giacomo 412
 
264 giacomo 413
        #ifdef CALIBRATE_USING_CMOS
414
          ll_calibrate_tsc_cmos();
415
        #else
416
          ll_calibrate_tsc();
417
        #endif  
418
 
120 giacomo 419
        rdtscll(init_tsc); // Read start TSC
420
        init_nsec = 0;
421
 
305 giacomo 422
        #ifdef __APIC__
299 giacomo 423
 
424
          rdmsr(APIC_BASE_MSR, msr_low_orig, tmp);
425
          wrmsr(APIC_BASE_MSR, msr_low_orig|(1<<11), 0);
426
 
301 giacomo 427
          clear_local_APIC();
299 giacomo 428
 
301 giacomo 429
          ll_calibrate_apic();
430
 
299 giacomo 431
          setup_local_APIC();
303 giacomo 432
 
433
          setup_APIC_timer();
299 giacomo 434
 
305 giacomo 435
        #endif
299 giacomo 436
 
305 giacomo 437
    #endif
120 giacomo 438
 
439
}
440
 
301 giacomo 441
void ll_restore_adv()
120 giacomo 442
{
302 giacomo 443
        /* Disable APIC */
305 giacomo 444
        #ifdef __APIC__
445
                unsigned int msr_low_orig, tmp;
301 giacomo 446
 
447
                cli();
448
 
449
                disable_APIC_timer();
450
 
451
                rdmsr(APIC_BASE_MSR, msr_low_orig, tmp);
452
                wrmsr(APIC_BASE_MSR, msr_low_orig&~(1<<11), 0);
453
 
454
                sti();
455
 
305 giacomo 456
        #endif
301 giacomo 457
 
120 giacomo 458
}