Subversion Repositories shark

Rev

Rev 121 | Rev 126 | 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
 
35
unsigned char use_tsc = 0; //Enable the TSC counter mode
36
unsigned char use_cmos = 0; //Enable the RTC correction
37
 
38
//Max single delta_clk_per_msec increment
39
#define MAX_DELTA_INK 10 
40
 
41
//Max delta_clk_per_msec before declare lost
42
//the CMOS sync
124 giacomo 43
#define MAX_DELTA_TOT 10000
120 giacomo 44
 
45
signed long long init_tsc;
46
signed long long init_nsec; //Warp around 292 years !!
47
signed long long clk_per_msec;
48
 
49
signed long last_delta_clk_per_msec;
50
signed long total_delta_clk_per_msec;
51
 
52
unsigned char save_CMOS_regA;
53
unsigned char save_CMOS_regB;
54
 
55
void HandlerIRQ8(void *p)
56
{
57
 
58
   unsigned char set;
59
 
60
   static unsigned long init_step = 0;
61
 
62
   signed long long actual_tsc;
63
 
64
   signed long long dt,dn;
65
   signed long delta_clk_per_msec;
66
 
67
   cli();
68
 
69
   CMOS_READ(0x0C,set);
70
 
71
   rdtscll(actual_tsc);
72
 
73
   //Delta TSC
74
   dt = actual_tsc - init_tsc;
75
 
76
   init_tsc = actual_tsc;
77
 
78
   UNSIGNED_TSC2NSEC(dt,&dn);
79
 
80
   //Offset
81
   init_nsec += dn;
82
 
83
   if (init_step < 5) {
84
           init_step++;
85
           return;
86
   }
87
 
88
   dn = dn % 1000000000 - 500000000;
89
 
90
   //Delta clk/msec
91
   delta_clk_per_msec = dn * clk_per_msec / (500000000 - dn);
92
 
93
   //clk_per_msec adjustment
94
   if (delta_clk_per_msec < 0) {
95
 
96
        if (delta_clk_per_msec > -MAX_DELTA_INK)
97
                clk_per_msec += delta_clk_per_msec;
98
        else
99
                clk_per_msec -= MAX_DELTA_INK;
100
   } else {
101
 
102
        if (delta_clk_per_msec < MAX_DELTA_INK)
103
                clk_per_msec += delta_clk_per_msec;
104
        else
105
                clk_per_msec += MAX_DELTA_INK;
106
   }
107
 
108
   if (delta_clk_per_msec > MAX_DELTA_TOT || delta_clk_per_msec < -MAX_DELTA_TOT) {
109
           message("Error: Delta_clk_per_msec |%ld| > %ld\n",\
110
                    (long)delta_clk_per_msec,(long)MAX_DELTA_TOT);
111
           ll_abort(10);
112
   }
113
 
114
   last_delta_clk_per_msec = delta_clk_per_msec;
115
   total_delta_clk_per_msec += delta_clk_per_msec;
116
 
117
   sti();
118
 
119
}
120
 
121
#define HZ 100
122
 
123
#ifdef CONFIG_MELAN
124
#  define CLOCK_TICK_RATE 1189200 /* AMD Elan has different frequency! */
125
#else
126
#  define CLOCK_TICK_RATE 1193180 /* Underlying HZ */
127
#endif
128
 
129
#define LATCH  ((CLOCK_TICK_RATE + HZ/2) / HZ)
130
 
131
#define CALIBRATE_LATCH (5 * LATCH)
132
#define CALIBRATE_TIME  (5 * 1000020/HZ)
133
 
134
//TSC Calibration (idea from the linux kernel code)
135
void ll_calibrate_tsc(void)
136
{
137
 
138
        signed long long start;
139
        signed long long end;
140
        signed long long dtsc;
141
 
142
        signed long start_8253, end_8253, delta_8253;
143
 
144
        cli();
145
 
146
        /* Set the Gate high, disable speaker */
147
        outp(0x61, (inp(0x61) & ~0x02) | 0x01);
148
 
149
        outp(0x43,0xB0);                        /* binary, mode 0, LSB/MSB, Ch 2 */
150
        outp(0x42,CALIBRATE_LATCH & 0xff);      /* LSB of count */
151
        outp(0x42,CALIBRATE_LATCH >> 8);        /* MSB of count */
152
 
153
        rdtscll(start);
154
        outp(0x43,0x00);
155
        start_8253 = inp(0x42);
156
        start_8253 |= inp(0x42) << 8;          
157
 
158
        do {
159
 
160
            outp(0x43,0x00);
161
            end_8253 = inp(0x42);
162
            end_8253 |= inp(0x42) << 8;
163
 
164
        } while (end_8253 > 10);
165
 
166
        rdtscll(end);
167
        outp(0x43,0x00);
168
        end_8253 = inp(0x42);
169
        end_8253 |= inp(0x42) << 8;
170
 
171
        //Delta TSC
172
        dtsc = end - start;
173
 
174
        //Delta PIT
175
        delta_8253 = start_8253 - end_8253 + 1;
176
 
177
        if (delta_8253 > 0xFFFF) {
178
                message("Error calculating Delta PIT\n");
179
                ll_abort(10);
180
        }
181
 
182
        message("Delta TSC               = %10ld\n",(long)dtsc);
183
 
184
        message("Delta PIT               = %10ld\n",(long)delta_8253);
185
 
186
        clk_per_msec = dtsc * CALIBRATE_LATCH * 1000 / delta_8253 / CALIBRATE_TIME;
187
 
188
        message("Calibrated Clk_per_msec = %10ld\n",(long)clk_per_msec);
189
 
190
        sti();
191
 
192
}
193
 
194
//Low level time read function
195
void read_timespec(struct timespec *tspec)
196
{
197
 
198
    signed long long actual_tsc;
199
    signed long long dt,dn;
200
 
124 giacomo 201
    if (clk_per_msec <= 0) {
202
            NULL_TIMESPEC(tspec);
203
            return;
204
    }
205
 
120 giacomo 206
    rdtscll(actual_tsc);
207
 
208
    tspec->tsc = actual_tsc;
209
 
210
    dt = actual_tsc - init_tsc;
211
 
212
    UNSIGNED_TSC2NSEC(dt,&dn);
213
 
214
    tspec->tv_sec = (init_nsec + dn) / 1000000000;
215
    tspec->tv_nsec = (init_nsec + dn) % 1000000000;
216
 
217
}
218
 
219
void ll_init_advtimer()
220
{
221
 
222
    if (use_tsc) {
223
 
224
        ll_calibrate_tsc();
225
 
226
        last_delta_clk_per_msec = 0;
227
        total_delta_clk_per_msec = 0;
228
 
229
        rdtscll(init_tsc); // Read start TSC
230
        init_nsec = 0;
231
 
232
        if (use_cmos) {
233
 
234
            message("CMOS adjustment enabled\n");
235
 
236
            cli();         
237
 
238
            irq_bind(8, HandlerIRQ8, INT_FORCE);
239
 
240
            CMOS_READ(0x0A,save_CMOS_regA);
241
            CMOS_READ(0x0B,save_CMOS_regB);
242
 
243
            CMOS_WRITE(0x0A,0x2F); // Set 2 Hz Periodic Interrupt
244
            CMOS_WRITE(0x0B,0x42); // Enable Interrupt
245
 
246
            irq_unmask(8);
247
 
248
            sti();
249
 
250
        }
251
 
252
    } else {
253
 
254
        use_cmos = 0;
255
 
256
   }
257
 
258
}
259
 
260
void restore_CMOS()
261
{
262
        if (use_cmos) {
263
                cli();
264
 
265
                irq_mask(8);
266
 
267
                CMOS_WRITE(0x0A,save_CMOS_regA);
268
                CMOS_WRITE(0x0B,save_CMOS_regB);
269
 
270
                sti();         
271
        }
272
}