Subversion Repositories shark

Rev

Rev 222 | 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
 
130 giacomo 35
unsigned char use_tsc = 1; //Enable the TSC counter mode
120 giacomo 36
unsigned char use_cmos = 0; //Enable the RTC correction
37
 
126 giacomo 38
//Max single delta_clk_per_msec increment = clk_per_msec / MAX_DIV_INK;
39
#define MAX_DIV_INK 30000
120 giacomo 40
 
41
signed long long init_tsc;
194 giacomo 42
signed long long * ptr_init_tsc = &init_tsc;
43
 
238 giacomo 44
signed long long init_nsec; //Wraparound 292 years
194 giacomo 45
signed long long * ptr_init_nsec = &init_nsec;
46
 
120 giacomo 47
signed long long clk_per_msec;
194 giacomo 48
signed long long * ptr_clk_per_msec = &clk_per_msec;
120 giacomo 49
 
50
signed long last_delta_clk_per_msec;
51
signed long total_delta_clk_per_msec;
52
 
53
unsigned char save_CMOS_regA;
54
unsigned char save_CMOS_regB;
55
 
56
void HandlerIRQ8(void *p)
57
{
58
 
59
   unsigned char set;
194 giacomo 60
 
61
   static unsigned long Mconst = 1000000;
120 giacomo 62
 
63
   static unsigned long init_step = 0;
64
 
126 giacomo 65
   signed long max_dcms = clk_per_msec / MAX_DIV_INK;
120 giacomo 66
 
194 giacomo 67
   static signed long long dn;
68
   static signed long long * ptr_dn = &dn;
120 giacomo 69
   signed long delta_clk_per_msec;
126 giacomo 70
 
120 giacomo 71
   cli();
72
 
73
   CMOS_READ(0x0C,set);
194 giacomo 74
 
75
   __asm__("rdtsc\n\t"
76
           "pushl %%eax\n\t"
77
           "pushl %%edx\n\t"
78
           "subl (%%edi),%%eax\n\t"
79
           "sbbl 4(%%edi),%%edx\n\t"
80
           "popl 4(%%edi)\n\t"
81
           "popl (%%edi)\n\t"
82
           "movl %%edx,%%ecx\n\t"
83
           "mull %4\n\t"
84
           "pushl %%eax\n\t"
85
           "movl %%ecx,%%eax\n\t"
86
           "movl %%edx,%%ecx\n\t"
87
           "mull %4\n\t"
88
           "addl %%ecx,%%eax\n\t"
89
           "adcl $0,%%edx\n\t"
90
           "divl (%%ebx)\n\t"
91
           "movl %%eax,4(%%esi)\n\t"
92
           "popl %%eax\n\t"
93
           "divl (%%ebx)\n\t"
94
           "movl %%eax,(%%esi)\n\t"
95
 
96
            :
97
            : "D" (ptr_init_tsc), "S" (ptr_dn), "b" (ptr_clk_per_msec),
98
              "c" (0), "m" (Mconst), "a" (0), "d" (0));
120 giacomo 99
 
100
   //Offset
101
   init_nsec += dn;
194 giacomo 102
 
120 giacomo 103
   if (init_step < 5) {
104
           init_step++;
222 giacomo 105
           sti();
120 giacomo 106
           return;
107
   }
108
 
109
   dn = dn % 1000000000 - 500000000;
110
 
111
   //Delta clk/msec
112
   delta_clk_per_msec = dn * clk_per_msec / (500000000 - dn);
113
 
114
   //clk_per_msec adjustment
115
   if (delta_clk_per_msec < 0) {
116
 
126 giacomo 117
        if (delta_clk_per_msec > -max_dcms)
120 giacomo 118
                clk_per_msec += delta_clk_per_msec;
119
        else
126 giacomo 120
                clk_per_msec -= max_dcms;
120 giacomo 121
   } else {
122
 
126 giacomo 123
        if (delta_clk_per_msec < max_dcms)
120 giacomo 124
                clk_per_msec += delta_clk_per_msec;
125
        else
126 giacomo 126
                clk_per_msec += max_dcms;
120 giacomo 127
   }
128
 
129
   last_delta_clk_per_msec = delta_clk_per_msec;
130
   total_delta_clk_per_msec += delta_clk_per_msec;
131
 
132
   sti();
133
 
134
}
135
 
136
#define HZ 100
137
 
138
#ifdef CONFIG_MELAN
139
#  define CLOCK_TICK_RATE 1189200 /* AMD Elan has different frequency! */
140
#else
141
#  define CLOCK_TICK_RATE 1193180 /* Underlying HZ */
142
#endif
143
 
144
#define LATCH  ((CLOCK_TICK_RATE + HZ/2) / HZ)
145
 
146
#define CALIBRATE_LATCH (5 * LATCH)
147
#define CALIBRATE_TIME  (5 * 1000020/HZ)
148
 
165 giacomo 149
#define COUNTER_END 50
150
 
120 giacomo 151
//TSC Calibration (idea from the linux kernel code)
152
void ll_calibrate_tsc(void)
153
{
154
 
155
        signed long long start;
156
        signed long long end;
157
        signed long long dtsc;
158
 
159
        signed long start_8253, end_8253, delta_8253;
160
 
161
        cli();
162
 
163
        /* Set the Gate high, disable speaker */
164
        outp(0x61, (inp(0x61) & ~0x02) | 0x01);
165
 
166
        outp(0x43,0xB0);                        /* binary, mode 0, LSB/MSB, Ch 2 */
238 giacomo 167
        outp(0x42,0xFF);                        /* LSB of count */
168
        outp(0x42,0xFF);                        /* MSB of count */
120 giacomo 169
 
170
        rdtscll(start);
171
        outp(0x43,0x00);
172
        start_8253 = inp(0x42);
173
        start_8253 |= inp(0x42) << 8;          
174
 
175
        do {
176
 
177
            outp(0x43,0x00);
178
            end_8253 = inp(0x42);
179
            end_8253 |= inp(0x42) << 8;
180
 
165 giacomo 181
        } while (end_8253 > COUNTER_END);
120 giacomo 182
 
183
        rdtscll(end);
184
        outp(0x43,0x00);
185
        end_8253 = inp(0x42);
186
        end_8253 |= inp(0x42) << 8;
187
 
238 giacomo 188
        outp(0x43,0xB0);                        /* binary, mode 0, LSB/MSB, Ch 2 */
189
        outp(0x42,0xFF);                        /* LSB of count */
190
        outp(0x42,0xFF);                        /* MSB of count */
191
 
192
        rdtscll(start);
193
        outp(0x43,0x00);
194
        start_8253 = inp(0x42);
195
        start_8253 |= inp(0x42) << 8;          
196
 
197
        do {
198
 
199
            outp(0x43,0x00);
200
            end_8253 = inp(0x42);
201
            end_8253 |= inp(0x42) << 8;
202
 
203
        } while (end_8253 > COUNTER_END);
204
 
205
        rdtscll(end);
206
        outp(0x43,0x00);
207
        end_8253 = inp(0x42);
208
        end_8253 |= inp(0x42) << 8;
209
 
120 giacomo 210
        //Delta TSC
211
        dtsc = end - start;
212
 
213
        //Delta PIT
214
        delta_8253 = start_8253 - end_8253 + 1;
215
 
216
        if (delta_8253 > 0xFFFF) {
217
                message("Error calculating Delta PIT\n");
218
                ll_abort(10);
219
        }
220
 
221
        message("Delta TSC               = %10ld\n",(long)dtsc);
222
 
223
        message("Delta PIT               = %10ld\n",(long)delta_8253);
224
 
225
        clk_per_msec = dtsc * CALIBRATE_LATCH * 1000 / delta_8253 / CALIBRATE_TIME;
226
 
227
        message("Calibrated Clk_per_msec = %10ld\n",(long)clk_per_msec);
228
 
229
        sti();
230
 
231
}
232
 
233
//Low level time read function
131 giacomo 234
void ll_read_timespec(struct timespec *tspec)
120 giacomo 235
{
236
 
194 giacomo 237
    static unsigned long Gconst = 1000000000;
238
    static unsigned long Mconst = 1000000;
120 giacomo 239
 
124 giacomo 240
    if (clk_per_msec <= 0) {
241
            NULL_TIMESPEC(tspec);
242
            return;
243
    }
244
 
194 giacomo 245
    __asm__("rdtsc\n\t"
246
            "subl (%%edi),%%eax\n\t"
247
            "sbbl 4(%%edi),%%edx\n\t"
248
            "movl %%edx,%%ecx\n\t"
249
            "mull %6\n\t"
250
            "pushl %%eax\n\t"
251
            "movl %%ecx,%%eax\n\t"
252
            "movl %%edx,%%ecx\n\t"
253
            "mull %6\n\t"
254
            "addl %%ecx,%%eax\n\t"
255
            "adcl $0,%%edx\n\t"
256
            "divl (%%ebx)\n\t"
257
            "movl %%eax,%%ecx\n\t"
258
            "popl %%eax\n\t"
259
            "divl (%%ebx)\n\t"
260
            "movl %%ecx,%%edx\n\t"
261
            "addl (%%esi),%%eax\n\t"
262
            "adcl 4(%%esi),%%edx\n\t"
263
            "divl %7\n\t"
264
 
265
            : "=a" (tspec->tv_sec), "=d" (tspec->tv_nsec)
266
            : "D" (ptr_init_tsc), "S" (ptr_init_nsec), "b" (ptr_clk_per_msec),
267
              "c" (0), "m" (Mconst), "m" (Gconst));
268
 
120 giacomo 269
}
270
 
271
void ll_init_advtimer()
272
{
273
 
274
    if (use_tsc) {
275
 
276
        ll_calibrate_tsc();
277
 
278
        last_delta_clk_per_msec = 0;
279
        total_delta_clk_per_msec = 0;
280
 
281
        rdtscll(init_tsc); // Read start TSC
282
        init_nsec = 0;
283
 
284
        if (use_cmos) {
285
 
131 giacomo 286
            message("CMOS adjustement enabled\n");
120 giacomo 287
 
288
            cli();         
289
 
290
            irq_bind(8, HandlerIRQ8, INT_FORCE);
291
 
292
            CMOS_READ(0x0A,save_CMOS_regA);
293
            CMOS_READ(0x0B,save_CMOS_regB);
294
 
295
            CMOS_WRITE(0x0A,0x2F); // Set 2 Hz Periodic Interrupt
296
            CMOS_WRITE(0x0B,0x42); // Enable Interrupt
297
 
298
            irq_unmask(8);
299
 
300
            sti();
301
 
302
        }
303
 
304
    } else {
305
 
306
        use_cmos = 0;
307
 
308
   }
309
 
310
}
311
 
131 giacomo 312
void ll_restore_CMOS()
120 giacomo 313
{
314
        if (use_cmos) {
315
                cli();
316
 
317
                irq_mask(8);
318
 
319
                CMOS_WRITE(0x0A,save_CMOS_regA);
320
                CMOS_WRITE(0x0B,save_CMOS_regB);
321
 
322
                sti();         
323
        }
324
}