Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include "u.h"
2
#include "../port/lib.h"
3
#include "mem.h"
4
#include "dat.h"
5
#include "fns.h"
6
#include "io.h"
7
 
8
/*
9
 *  8253 timer
10
 */
11
enum
12
{
13
	T0cntr=	0x40,		/* counter ports */
14
	T1cntr=	0x41,		/* ... */
15
	T2cntr=	0x42,		/* ... */
16
	Tmode=	0x43,		/* mode port (control word register) */
17
	T2ctl=	0x61,		/* counter 2 control port */
18
 
19
	/* commands */
20
	Latch0=	0x00,		/* latch counter 0's value */
21
	Load0l=	0x10,		/* load counter 0's lsb */
22
	Load0m=	0x20,		/* load counter 0's msb */
23
	Load0=	0x30,		/* load counter 0 with 2 bytes */
24
 
25
	Latch1=	0x40,		/* latch counter 1's value */
26
	Load1l=	0x50,		/* load counter 1's lsb */
27
	Load1m=	0x60,		/* load counter 1's msb */
28
	Load1=	0x70,		/* load counter 1 with 2 bytes */
29
 
30
	Latch2=	0x80,		/* latch counter 2's value */
31
	Load2l=	0x90,		/* load counter 2's lsb */
32
	Load2m=	0xa0,		/* load counter 2's msb */
33
	Load2=	0xb0,		/* load counter 2 with 2 bytes */
34
 
35
	/* 8254 read-back command: everything > pc-at has an 8254 */
36
	Rdback=	0xc0,		/* readback counters & status */
37
	Rdnstat=0x10,		/* don't read status */
38
	Rdncnt=	0x20,		/* don't read counter value */
39
	Rd0cntr=0x02,		/* read back for which counter */
40
	Rd1cntr=0x04,
41
	Rd2cntr=0x08,
42
 
43
	/* modes */
44
	ModeMsk=0xe,
45
	Square=	0x6,		/* periodic square wave */
46
	Trigger=0x0,		/* interrupt on terminal count */
47
	Sstrobe=0x8,		/* software triggered strobe */
48
 
49
	/* T2ctl bits */
50
	T2gate=	(1<<0),		/* enable T2 counting */
51
	T2spkr=	(1<<1),		/* connect T2 out to speaker */
52
	T2out=	(1<<5),		/* output of T2 */
53
 
54
	Freq=	1193182,	/* Real clock frequency */
55
	Tickshift=8,		/* extra accuracy */
56
	MaxPeriod=Freq/HZ,
57
	MinPeriod=Freq/(100*HZ),
58
 
59
	Wdogms	= 200,		/* ms between strokes */
60
};
61
 
62
typedef struct I8253 I8253;
63
struct I8253
64
{
65
	Lock;
66
	ulong	period;		/* current clock period */
67
	int	enabled;
68
	uvlong	hz;
69
 
70
	ushort	last;		/* last value of clock 1 */
71
	uvlong	ticks;		/* cumulative ticks of counter 1 */
72
 
73
	ulong	periodset;
74
};
75
I8253 i8253;
76
 
77
void
78
i8253init(void)
79
{
80
	int loops, x;
81
 
82
	ioalloc(T0cntr, 4, 0, "i8253");
83
	ioalloc(T2ctl, 1, 0, "i8253.cntr2ctl");
84
 
85
	i8253.period = Freq/HZ;
86
 
87
	/*
88
	 *  enable a 1/HZ interrupt for providing scheduling interrupts
89
	 */
90
	outb(Tmode, Load0|Square);
91
	outb(T0cntr, (Freq/HZ));	/* low byte */
92
	outb(T0cntr, (Freq/HZ)>>8);	/* high byte */
93
 
94
	/*
95
	 *  enable a longer period counter to use as a clock
96
	 */
97
	outb(Tmode, Load2|Square);
98
	outb(T2cntr, 0);		/* low byte */
99
	outb(T2cntr, 0);		/* high byte */
100
	x = inb(T2ctl);
101
	x |= T2gate;
102
	outb(T2ctl, x);
103
 
104
	/*
105
	 * Introduce a little delay to make sure the count is
106
	 * latched and the timer is counting down; with a fast
107
	 * enough processor this may not be the case.
108
	 * The i8254 (which this probably is) has a read-back
109
	 * command which can be used to make sure the counting
110
	 * register has been written into the counting element.
111
	 */
112
	x = (Freq/HZ);
113
	for(loops = 0; loops < 100000 && x >= (Freq/HZ); loops++){
114
		outb(Tmode, Latch0);
115
		x = inb(T0cntr);
116
		x |= inb(T0cntr)<<8;
117
	}
118
}
119
 
120
/*
121
 * if the watchdog is running and we're on cpu 0 and ignoring (clock)
122
 * interrupts, disable the watchdog temporarily so that the (presumed)
123
 * long-running loop to follow will not trigger an NMI.
124
 * wdogresume restarts the watchdog if wdogpause stopped it.
125
 */
126
static int
127
wdogpause(void)
128
{
129
	int turndogoff;
130
 
131
	turndogoff = watchdogon && m->machno == 0 && !islo();
132
	if (turndogoff) {
133
		watchdog->disable();
134
		watchdogon = 0;
135
	}
136
	return turndogoff;
137
}
138
 
139
static void
140
wdogresume(int resume)
141
{
142
	if (resume) {
143
		watchdog->enable();
144
		watchdogon = 1;
145
	}
146
}
147
 
148
void
149
guesscpuhz(int aalcycles)
150
{
151
	int loops, incr, x, y, dogwason;
152
	uvlong a, b, cpufreq;
153
 
154
	dogwason = wdogpause();		/* don't get NMI while busy looping */
155
 
156
	/* find biggest loop that doesn't wrap */
157
	incr = 16000000/(aalcycles*HZ*2);
158
	x = 2000;
159
	for(loops = incr; loops < 64*1024; loops += incr) {
160
 
161
		/*
162
		 *  measure time for the loop
163
		 *
164
		 *			MOVL	loops,CX
165
		 *	aaml1:	 	AAM
166
		 *			LOOP	aaml1
167
		 *
168
		 *  the time for the loop should be independent of external
169
		 *  cache and memory system since it fits in the execution
170
		 *  prefetch buffer.
171
		 *
172
		 */
173
		outb(Tmode, Latch0);
174
		cycles(&a);
175
		x = inb(T0cntr);
176
		x |= inb(T0cntr)<<8;
177
		aamloop(loops);
178
		outb(Tmode, Latch0);
179
		cycles(&b);
180
		y = inb(T0cntr);
181
		y |= inb(T0cntr)<<8;
182
		x -= y;
183
 
184
		if(x < 0)
185
			x += Freq/HZ;
186
 
187
		if(x > Freq/(3*HZ))
188
			break;
189
	}
190
	wdogresume(dogwason);
191
 
192
	/*
193
 	 *  figure out clock frequency and a loop multiplier for delay().
194
	 *  n.b. counter goes up by 2*Freq
195
	 */
196
	if(x == 0)
197
		x = 1;			/* avoid division by zero on vmware 7 */
198
	cpufreq = (vlong)loops*((aalcycles*2*Freq)/x);
199
	m->loopconst = (cpufreq/1000)/aalcycles;	/* AAM+LOOP's for 1 ms */
200
 
201
	if(m->havetsc && a != b){  /* a == b means virtualbox has confused us */
202
		/* counter goes up by 2*Freq */
203
		b = (b-a)<<1;
204
		b *= Freq;
205
		b /= x;
206
 
207
		/*
208
		 *  round to the nearest megahz
209
		 */
210
		m->cpumhz = (b+500000)/1000000L;
211
		m->cpuhz = b;
212
		m->cyclefreq = b;
213
	} else {
214
		/*
215
		 *  add in possible 0.5% error and convert to MHz
216
		 */
217
		m->cpumhz = (cpufreq + cpufreq/200)/1000000;
218
		m->cpuhz = cpufreq;
219
	}
220
 
221
	/* don't divide by zero in trap.c */
222
	if (m->cpumhz == 0)
223
		panic("guesscpuhz: zero m->cpumhz");
224
	i8253.hz = Freq<<Tickshift;
225
}
226
 
227
void
228
i8253timerset(uvlong next)
229
{
230
	long period;
231
	ulong want;
232
	ulong now;
233
 
234
	period = MaxPeriod;
235
	if(next != 0){
236
		want = next>>Tickshift;
237
		now = i8253.ticks;	/* assuming whomever called us just did fastticks() */
238
 
239
		period = want - now;
240
		if(period < MinPeriod)
241
			period = MinPeriod;
242
		else if(period > MaxPeriod)
243
			period = MaxPeriod;
244
	}
245
 
246
	/* hysteresis */
247
	if(i8253.period != period){
248
		ilock(&i8253);
249
		/* load new value */
250
		outb(Tmode, Load0|Square);
251
		outb(T0cntr, period);		/* low byte */
252
		outb(T0cntr, period >> 8);		/* high byte */
253
 
254
		/* remember period */
255
		i8253.period = period;
256
		i8253.periodset++;
257
		iunlock(&i8253);
258
	}
259
}
260
 
261
static void
262
i8253clock(Ureg* ureg, void*)
263
{
264
	timerintr(ureg, 0);
265
}
266
 
267
void
268
i8253enable(void)
269
{
270
	i8253.enabled = 1;
271
	i8253.period = Freq/HZ;
272
	intrenable(IrqCLOCK, i8253clock, 0, BUSUNKNOWN, "clock");
273
}
274
 
275
void
276
i8253link(void)
277
{
278
}
279
 
280
/*
281
 *  return the total ticks of counter 2.  We shift by
282
 *  8 to give timesync more wriggle room for interpretation
283
 *  of the frequency
284
 */
285
uvlong
286
i8253read(uvlong *hz)
287
{
288
	ushort y, x;
289
	uvlong ticks;
290
 
291
	if(hz)
292
		*hz = i8253.hz;
293
 
294
	ilock(&i8253);
295
	outb(Tmode, Latch2);
296
	y = inb(T2cntr);
297
	y |= inb(T2cntr)<<8;
298
 
299
	if(y < i8253.last)
300
		x = i8253.last - y;
301
	else {
302
		x = i8253.last + (0x10000 - y);
303
		if (x > 3*MaxPeriod) {
304
			outb(Tmode, Load2|Square);
305
			outb(T2cntr, 0);		/* low byte */
306
			outb(T2cntr, 0);		/* high byte */
307
			y = 0xFFFF;
308
			x = i8253.period;
309
		}
310
	}
311
	i8253.last = y;
312
	i8253.ticks += x>>1;
313
	ticks = i8253.ticks;
314
	iunlock(&i8253);
315
 
316
	return ticks<<Tickshift;
317
}
318
 
319
void
320
delay(int millisecs)
321
{
322
	if (millisecs > 10*1000)
323
		iprint("delay(%d) from %#p\n", millisecs,
324
			getcallerpc(&millisecs));
325
	if (watchdogon && m->machno == 0 && !islo())
326
		for (; millisecs > Wdogms; millisecs -= Wdogms) {
327
			delay(Wdogms);
328
			watchdog->restart();
329
		}
330
	millisecs *= m->loopconst;
331
	if(millisecs <= 0)
332
		millisecs = 1;
333
	aamloop(millisecs);
334
}
335
 
336
void
337
microdelay(int microsecs)
338
{
339
	if (watchdogon && m->machno == 0 && !islo())
340
		for (; microsecs > Wdogms*1000; microsecs -= Wdogms*1000) {
341
			delay(Wdogms);
342
			watchdog->restart();
343
		}
344
	microsecs *= m->loopconst;
345
	microsecs /= 1000;
346
	if(microsecs <= 0)
347
		microsecs = 1;
348
	aamloop(microsecs);
349
}
350
 
351
/*  
352
 *  performance measurement ticks.  must be low overhead.
353
 *  doesn't have to count over a second.
354
 */
355
ulong
356
perfticks(void)
357
{
358
	uvlong x;
359
 
360
	if(m->havetsc)
361
		cycles(&x);
362
	else
363
		x = 0;
364
	return x;
365
}