Subversion Repositories shark

Rev

Rev 255 | Rev 299 | 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>
32
#include <ll/sys/ll/event.h>
33
#include <ll/sys/ll/time.h>
34
 
264 giacomo 35
#define CALIBRATE_USING_CMOS
36
 
130 giacomo 37
unsigned char use_tsc = 1; //Enable the TSC counter mode
120 giacomo 38
unsigned char use_cmos = 0; //Enable the RTC correction
39
 
126 giacomo 40
//Max single delta_clk_per_msec increment = clk_per_msec / MAX_DIV_INK;
41
#define MAX_DIV_INK 30000
120 giacomo 42
 
43
signed long long init_tsc;
194 giacomo 44
signed long long * ptr_init_tsc = &init_tsc;
45
 
238 giacomo 46
signed long long init_nsec; //Wraparound 292 years
194 giacomo 47
signed long long * ptr_init_nsec = &init_nsec;
48
 
120 giacomo 49
signed long long clk_per_msec;
194 giacomo 50
signed long long * ptr_clk_per_msec = &clk_per_msec;
120 giacomo 51
 
52
signed long last_delta_clk_per_msec;
53
signed long total_delta_clk_per_msec;
54
 
55
unsigned char save_CMOS_regA;
56
unsigned char save_CMOS_regB;
57
 
242 giacomo 58
//#define IRQ8_DEBUG
59
 
120 giacomo 60
void HandlerIRQ8(void *p)
61
{
62
 
63
   unsigned char set;
194 giacomo 64
 
65
   static unsigned long Mconst = 1000000;
120 giacomo 66
 
67
   static unsigned long init_step = 0;
68
 
126 giacomo 69
   signed long max_dcms = clk_per_msec / MAX_DIV_INK;
120 giacomo 70
 
194 giacomo 71
   static signed long long dn;
72
   static signed long long * ptr_dn = &dn;
120 giacomo 73
   signed long delta_clk_per_msec;
126 giacomo 74
 
120 giacomo 75
   cli();
242 giacomo 76
 
77
   #ifdef IRQ8_DEBUG
78
     message("(IRQ8");
79
   #endif   
80
 
120 giacomo 81
   CMOS_READ(0x0C,set);
194 giacomo 82
 
242 giacomo 83
   __asm__("xorl %%eax,%%eax\n\t"
84
           "cpuid\n\t"
85
           "rdtsc\n\t"
194 giacomo 86
           "pushl %%eax\n\t"
87
           "pushl %%edx\n\t"
242 giacomo 88
           "pushl %%eax\n\t"
89
           "pushl %%edx\n\t"
90
           "xorl %%eax,%%eax\n\t"
91
           "cpuid\n\t"
92
           "popl %%edx\n\t"
93
           "popl %%eax\n\t"
194 giacomo 94
           "subl (%%edi),%%eax\n\t"
95
           "sbbl 4(%%edi),%%edx\n\t"
96
           "popl 4(%%edi)\n\t"
97
           "popl (%%edi)\n\t"
98
           "movl %%edx,%%ecx\n\t"
99
           "mull %4\n\t"
100
           "pushl %%eax\n\t"
101
           "movl %%ecx,%%eax\n\t"
102
           "movl %%edx,%%ecx\n\t"
103
           "mull %4\n\t"
104
           "addl %%ecx,%%eax\n\t"
105
           "adcl $0,%%edx\n\t"
242 giacomo 106
           "movl %7,%%ebx\n\t"
194 giacomo 107
           "divl (%%ebx)\n\t"
108
           "movl %%eax,4(%%esi)\n\t"
109
           "popl %%eax\n\t"
110
           "divl (%%ebx)\n\t"
111
           "movl %%eax,(%%esi)\n\t"
112
 
113
            :
242 giacomo 114
            : "D" (ptr_init_tsc), "S" (ptr_dn), "b" (0),
115
              "c" (0), "m" (Mconst), "a" (0), "d" (0), "m" (ptr_clk_per_msec));
120 giacomo 116
 
117
   //Offset
118
   init_nsec += dn;
194 giacomo 119
 
120 giacomo 120
   if (init_step < 5) {
121
           init_step++;
242 giacomo 122
           #ifdef IRQ8_DEBUG
123
             message(")");
124
           #endif
125
 
244 giacomo 126
           sti();
127
 
120 giacomo 128
           return;
129
   }
130
 
131
   dn = dn % 1000000000 - 500000000;
132
 
133
   //Delta clk/msec
134
   delta_clk_per_msec = dn * clk_per_msec / (500000000 - dn);
135
 
136
   //clk_per_msec adjustment
137
   if (delta_clk_per_msec < 0) {
138
 
126 giacomo 139
        if (delta_clk_per_msec > -max_dcms)
120 giacomo 140
                clk_per_msec += delta_clk_per_msec;
141
        else
126 giacomo 142
                clk_per_msec -= max_dcms;
120 giacomo 143
   } else {
144
 
126 giacomo 145
        if (delta_clk_per_msec < max_dcms)
120 giacomo 146
                clk_per_msec += delta_clk_per_msec;
147
        else
126 giacomo 148
                clk_per_msec += max_dcms;
120 giacomo 149
   }
150
 
151
   last_delta_clk_per_msec = delta_clk_per_msec;
152
   total_delta_clk_per_msec += delta_clk_per_msec;
153
 
242 giacomo 154
   #ifdef IRQ8_DEBUG
155
      message(")");
156
   #endif
157
 
120 giacomo 158
   sti();
159
 
160
}
161
 
162
#ifdef CONFIG_MELAN
163
#  define CLOCK_TICK_RATE 1189200 /* AMD Elan has different frequency! */
164
#else
249 giacomo 165
#  define CLOCK_TICK_RATE 1193182 /* Underlying HZ */
120 giacomo 166
#endif
167
 
248 giacomo 168
#define COUNTER_END 100
120 giacomo 169
 
245 giacomo 170
#define barrier() __asm__ __volatile__("" ::: "memory");
171
 
120 giacomo 172
//TSC Calibration (idea from the linux kernel code)
173
void ll_calibrate_tsc(void)
174
{
175
 
176
        signed long long start;
177
        signed long long end;
244 giacomo 178
        signed long long dtsc;
120 giacomo 179
 
244 giacomo 180
        signed long start_8253, end_8253, delta_8253;
120 giacomo 181
 
182
        cli();
183
 
248 giacomo 184
        outp(0x61, (inp(0x61) & ~0x02) | 0x01);
120 giacomo 185
 
186
        outp(0x43,0xB0);                        /* binary, mode 0, LSB/MSB, Ch 2 */
238 giacomo 187
        outp(0x42,0xFF);                        /* LSB of count */
188
        outp(0x42,0xFF);                        /* MSB of count */
242 giacomo 189
 
245 giacomo 190
        barrier();
191
        rdtscll(start);
192
        barrier();
243 giacomo 193
        outp(0x43,0x00);
194
        start_8253 = inp(0x42);
195
        start_8253 |= inp(0x42) << 8;
245 giacomo 196
        barrier();
264 giacomo 197
        rdtscll(start);
198
        barrier();
243 giacomo 199
 
200
        do {
201
 
202
            outp(0x43,0x00);
203
            end_8253 = inp(0x42);
204
            end_8253 |= inp(0x42) << 8;
205
 
206
        } while (end_8253 > COUNTER_END);
207
 
245 giacomo 208
        barrier();
209
        rdtscll(end);
210
        barrier();
243 giacomo 211
        outp(0x43,0x00);
212
        end_8253 = inp(0x42);
213
        end_8253 |= inp(0x42) << 8;
245 giacomo 214
        barrier();
264 giacomo 215
        rdtscll(end);
216
        barrier();
243 giacomo 217
 
218
        //Delta TSC
244 giacomo 219
        dtsc = end - start;
243 giacomo 220
 
221
        //Delta PIT
264 giacomo 222
        delta_8253 = start_8253 - end_8253;
243 giacomo 223
 
242 giacomo 224
        if (delta_8253 > 0x20000) {
120 giacomo 225
                message("Error calculating Delta PIT\n");
226
                ll_abort(10);
227
        }
228
 
229
        message("Delta TSC               = %10ld\n",(long)dtsc);
230
 
231
        message("Delta PIT               = %10ld\n",(long)delta_8253);
232
 
252 giacomo 233
        clk_per_msec = dtsc * CLOCK_TICK_RATE / delta_8253 / 1000;
120 giacomo 234
 
235
        message("Calibrated Clk_per_msec = %10ld\n",(long)clk_per_msec);
236
 
237
        sti();
238
 
239
}
240
 
264 giacomo 241
#define CMOS_INIT  0
242
#define CMOS_BEGIN 1
243
#define CMOS_START 2
244
#define CMOS_END   3
245
 
246
int cmos_calibrate_status = CMOS_INIT;
247
signed long long irq8_start;
248
signed long long irq8_end;
249
 
250
void calibrate_IRQ8(void *p)
251
{
252
 
253
  unsigned char set;
254
 
255
  cli();
256
 
257
  CMOS_READ(0x0C,set);
258
 
259
  barrier();
260
  rdtscll(irq8_end);
261
  barrier();
262
 
263
  if (cmos_calibrate_status == CMOS_START) {
264
    cmos_calibrate_status = CMOS_END;
265
  }
266
 
267
  if (cmos_calibrate_status == CMOS_BEGIN) {
268
    irq8_start = irq8_end;
269
    cmos_calibrate_status = CMOS_START;
270
  }
271
 
272
  if (cmos_calibrate_status == CMOS_INIT) {
273
    cmos_calibrate_status = CMOS_BEGIN;
274
  }
275
 
276
  sti();
277
 
278
}
279
 
280
//TSC Calibration using RTC
281
void ll_calibrate_tsc_cmos(void)
282
{
283
 
284
  signed long long dtsc;
285
 
286
  cli();           
287
 
288
  irq_bind(8, calibrate_IRQ8, INT_FORCE);
289
 
290
  CMOS_READ(0x0A,save_CMOS_regA);
291
  CMOS_READ(0x0B,save_CMOS_regB);
292
 
293
  CMOS_WRITE(0x0A,0x2F); // Set 2 Hz Periodic Interrupt
294
  CMOS_WRITE(0x0B,0x42); // Enable Interrupt
295
 
296
  irq_unmask(8);
297
 
298
  sti();
299
 
300
  while (cmos_calibrate_status != CMOS_END) {
301
    barrier();
302
  }
303
 
304
  dtsc = irq8_end - irq8_start;
305
 
306
  clk_per_msec = dtsc / 500;
307
 
308
  message("Calibrated Clk_per_msec = %10ld\n",(long)clk_per_msec);
309
 
310
  cli();
311
 
312
  irq_mask(8);
313
 
314
  CMOS_WRITE(0x0A,save_CMOS_regA);
315
  CMOS_WRITE(0x0B,save_CMOS_regB);
316
 
317
  sti();
318
 
319
}
320
 
120 giacomo 321
//Low level time read function
131 giacomo 322
void ll_read_timespec(struct timespec *tspec)
120 giacomo 323
{
324
 
194 giacomo 325
    static unsigned long Gconst = 1000000000;
326
    static unsigned long Mconst = 1000000;
120 giacomo 327
 
124 giacomo 328
    if (clk_per_msec <= 0) {
329
            NULL_TIMESPEC(tspec);
330
            return;
331
    }
332
 
242 giacomo 333
    __asm__("xorl %%eax,%%eax\n\t"
334
            "cpuid\n\t"  
335
            "rdtsc\n\t"
336
            "pushl %%eax\n\t"
337
            "pushl %%edx\n\t"
338
            "xorl %%eax,%%eax\n\t"
339
            "cpuid\n\t"
340
            "popl %%edx\n\t"
341
            "popl %%eax\n\t"
342
            "subl (%%edi),%%eax\n\t"
194 giacomo 343
            "sbbl 4(%%edi),%%edx\n\t"
344
            "movl %%edx,%%ecx\n\t"
345
            "mull %6\n\t"
346
            "pushl %%eax\n\t"
347
            "movl %%ecx,%%eax\n\t"
348
            "movl %%edx,%%ecx\n\t"
349
            "mull %6\n\t"
350
            "addl %%ecx,%%eax\n\t"
351
            "adcl $0,%%edx\n\t"
242 giacomo 352
            "movl %8,%%ebx\n\t"
194 giacomo 353
            "divl (%%ebx)\n\t"
354
            "movl %%eax,%%ecx\n\t"
355
            "popl %%eax\n\t"
356
            "divl (%%ebx)\n\t"
357
            "movl %%ecx,%%edx\n\t"
358
            "addl (%%esi),%%eax\n\t"
359
            "adcl 4(%%esi),%%edx\n\t"
360
            "divl %7\n\t"
361
 
362
            : "=a" (tspec->tv_sec), "=d" (tspec->tv_nsec)
242 giacomo 363
            : "D" (ptr_init_tsc), "S" (ptr_init_nsec), "b" (0),
364
              "c" (0), "m" (Mconst), "m" (Gconst), "m" (ptr_clk_per_msec));
194 giacomo 365
 
120 giacomo 366
}
367
 
368
void ll_init_advtimer()
369
{
370
 
371
    if (use_tsc) {
372
 
264 giacomo 373
        #ifdef CALIBRATE_USING_CMOS
374
          ll_calibrate_tsc_cmos();
375
        #else
376
          ll_calibrate_tsc();
377
        #endif  
378
 
120 giacomo 379
        last_delta_clk_per_msec = 0;
380
        total_delta_clk_per_msec = 0;
381
 
382
        rdtscll(init_tsc); // Read start TSC
383
        init_nsec = 0;
384
 
385
        if (use_cmos) {
386
 
131 giacomo 387
            message("CMOS adjustement enabled\n");
120 giacomo 388
 
389
            cli();         
390
 
391
            irq_bind(8, HandlerIRQ8, INT_FORCE);
392
 
393
            CMOS_READ(0x0A,save_CMOS_regA);
394
            CMOS_READ(0x0B,save_CMOS_regB);
395
 
396
            CMOS_WRITE(0x0A,0x2F); // Set 2 Hz Periodic Interrupt
397
            CMOS_WRITE(0x0B,0x42); // Enable Interrupt
398
 
399
            irq_unmask(8);
400
 
401
            sti();
402
 
403
        }
404
 
405
    } else {
406
 
407
        use_cmos = 0;
408
 
409
   }
410
 
411
}
412
 
131 giacomo 413
void ll_restore_CMOS()
120 giacomo 414
{
415
        if (use_cmos) {
416
                cli();
417
 
418
                irq_mask(8);
419
 
420
                CMOS_WRITE(0x0A,save_CMOS_regA);
421
                CMOS_WRITE(0x0B,save_CMOS_regB);
422
 
423
                sti();         
424
        }
425
}