Subversion Repositories shark

Rev

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