tsc.c revision 3185
1/*-
2 * Copyright (c) 1990 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * William Jolitz and Don Ahn.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 *	from: @(#)clock.c	7.2 (Berkeley) 5/12/91
37 *	$Id: clock.c,v 1.21 1994/09/20 21:20:46 bde Exp $
38 */
39
40/*
41 * inittodr, settodr and support routines written
42 * by Christoph Robitschko <chmr@edvz.tu-graz.ac.at>
43 *
44 * reintroduced and updated by Chris Stenton <chris@gnome.co.uk> 8/10/94
45 */
46
47/*
48 * Primitive clock interrupt routines.
49 */
50#include <sys/param.h>
51#include <sys/systm.h>
52#include <sys/time.h>
53#include <sys/kernel.h>
54#include <machine/frame.h>
55#include <i386/isa/icu.h>
56#include <i386/isa/isa.h>
57#include <i386/isa/rtc.h>
58#include <i386/isa/timerreg.h>
59
60/*
61 * 32-bit time_t's can't reach leap years before 1904 or after 2036, so we
62 * can use a simple formula for leap years.
63 */
64#define	LEAPYEAR(y) ((u_int)(y) % 4 == 0)
65#define DAYSPERYEAR   (31+28+31+30+31+30+31+31+30+31+30+31)
66
67/* X-tals being what they are, it's nice to be able to fudge this one... */
68/* Note, the name changed here from XTALSPEED to TIMER_FREQ rgrimes 4/26/93 */
69#ifndef TIMER_FREQ
70#define	TIMER_FREQ	1193182	/* XXX - should be in isa.h */
71#endif
72#define TIMER_DIV(x) ((TIMER_FREQ+(x)/2)/(x))
73
74static	int beeping;
75int 	timer0_divisor = TIMER_DIV(100);	/* XXX should be hz */
76u_int 	timer0_prescale;
77int	adjkerntz = 0;	/* offset from CMOS clock */
78static 	char timer0_state = 0, timer2_state = 0;
79static 	char timer0_reprogram = 0;
80static 	void (*timer_func)() = hardclock;
81static 	void (*new_function)();
82static 	u_int new_rate;
83static 	u_int hardclock_divisor;
84static	const u_char daysinmonth[] = {31,28,31,30,31,30,31,31,30,31,30,31};
85static 	u_char rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
86
87#ifdef I586_CPU
88int pentium_mhz = 0;
89#endif
90
91#if 0
92void
93clkintr(struct clockframe frame)
94{
95	hardclock(&frame);
96}
97#else
98void
99clkintr(struct clockframe frame)
100{
101	timer_func(&frame);
102	switch (timer0_state) {
103	case 0:
104		break;
105	case 1:
106		if ((timer0_prescale+=timer0_divisor) >= hardclock_divisor) {
107			hardclock(&frame);
108			timer0_prescale = 0;
109		}
110		break;
111	case 2:
112		disable_intr();
113		outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
114		outb(TIMER_CNTR0, TIMER_DIV(new_rate)%256);
115		outb(TIMER_CNTR0, TIMER_DIV(new_rate)/256);
116		enable_intr();
117		timer0_divisor = TIMER_DIV(new_rate);
118		timer0_prescale = 0;
119		timer_func = new_function;
120		timer0_state = 1;
121		break;
122	case 3:
123		if ((timer0_prescale+=timer0_divisor) >= hardclock_divisor) {
124			hardclock(&frame);
125			disable_intr();
126			outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
127			outb(TIMER_CNTR0, TIMER_DIV(hz)%256);
128			outb(TIMER_CNTR0, TIMER_DIV(hz)/256);
129			enable_intr();
130			timer0_divisor = TIMER_DIV(hz);
131			timer0_prescale = 0;
132			timer_func = hardclock;;
133			timer0_state = 0;
134		}
135		break;
136	}
137}
138#endif
139
140int
141acquire_timer0(int rate, void (*function)() )
142{
143	if (timer0_state || !function)
144		return -1;
145	new_function = function;
146	new_rate = rate;
147	timer0_state = 2;
148	return 0;
149}
150
151int
152acquire_timer2(int mode)
153{
154	if (timer2_state)
155		return -1;
156	timer2_state = 1;
157	outb(TIMER_MODE, TIMER_SEL2 | (mode &0x3f));
158	return 0;
159}
160
161int
162release_timer0()
163{
164	if (!timer0_state)
165		return -1;
166	timer0_state = 3;
167	return 0;
168}
169
170int
171release_timer2()
172{
173	if (!timer2_state)
174		return -1;
175	timer2_state = 0;
176	outb(TIMER_MODE, TIMER_SEL2|TIMER_SQWAVE|TIMER_16BIT);
177	return 0;
178}
179
180/*
181 * This routine receives statistical clock interrupts from the RTC.
182 * As explained above, these occur at 128 interrupts per second.
183 * When profiling, we receive interrupts at a rate of 1024 Hz.
184 *
185 * This does not actually add as much overhead as it sounds, because
186 * when the statistical clock is active, the hardclock driver no longer
187 * needs to keep (inaccurate) statistics on its own.  This decouples
188 * statistics gathering from scheduling interrupts.
189 *
190 * The RTC chip requires that we read status register C (RTC_INTR)
191 * to acknowledge an interrupt, before it will generate the next one.
192 */
193void
194rtcintr(struct clockframe frame)
195{
196	u_char stat;
197	stat = rtcin(RTC_INTR);
198	if(stat & RTCIR_PERIOD) {
199		statclock(&frame);
200	}
201}
202
203#ifdef DEBUG
204void
205printrtc(void)
206{
207	outb(IO_RTC, RTC_STATUSA);
208	printf("RTC status A = %x", inb(IO_RTC+1));
209	outb(IO_RTC, RTC_STATUSB);
210	printf(", B = %x", inb(IO_RTC+1));
211	outb(IO_RTC, RTC_INTR);
212	printf(", C = %x\n", inb(IO_RTC+1));
213}
214#endif
215
216static int
217getit()
218{
219	int high, low;
220
221	disable_intr();
222	/* select timer0 and latch counter value */
223	outb(TIMER_MODE, TIMER_SEL0);
224	low = inb(TIMER_CNTR0);
225	high = inb(TIMER_CNTR0);
226	enable_intr();
227	return ((high << 8) | low);
228}
229
230#ifdef I586_CPU
231static long long cycles_per_sec = 0;
232
233/*
234 * Figure out how fast the cyclecounter runs.  This must be run with
235 * clock interrupts disabled, but with the timer/counter programmed
236 * and running.
237 */
238void
239calibrate_cyclecounter(void)
240{
241	volatile long edx, eax, lasteax, lastedx;
242
243	__asm __volatile(".byte 0x0f, 0x31" : "=a"(lasteax), "=d"(lastedx) : );
244	DELAY(1000000);
245	__asm __volatile(".byte 0x0f, 0x31" : "=a"(eax), "=d"(edx) : );
246
247	/*
248	 * This assumes that you will never have a clock rate higher
249	 * than 4GHz, probably a good assumption.
250	 */
251	cycles_per_sec = (long long)edx + eax;
252	cycles_per_sec -= (long long)lastedx + lasteax;
253	pentium_mhz = ((long)cycles_per_sec + 500000) / 1000000; /* round up */
254}
255#endif
256
257/*
258 * Wait "n" microseconds.
259 * Relies on timer 1 counting down from (TIMER_FREQ / hz)
260 * Note: timer had better have been programmed before this is first used!
261 */
262void
263DELAY(int n)
264{
265	int prev_tick, tick, ticks_left, sec, usec;
266
267#ifdef DELAYDEBUG
268	int getit_calls = 1;
269	int n1;
270	static int state = 0;
271
272	if (state == 0) {
273		state = 1;
274		for (n1 = 1; n1 <= 10000000; n1 *= 10)
275			DELAY(n1);
276		state = 2;
277	}
278	if (state == 1)
279		printf("DELAY(%d)...", n);
280#endif
281	/*
282	 * Read the counter first, so that the rest of the setup overhead is
283	 * counted.  Guess the initial overhead is 20 usec (on most systems it
284	 * takes about 1.5 usec for each of the i/o's in getit().  The loop
285	 * takes about 6 usec on a 486/33 and 13 usec on a 386/20.  The
286	 * multiplications and divisions to scale the count take a while).
287	 */
288	prev_tick = getit(0, 0);
289	n -= 20;
290	/*
291	 * Calculate (n * (TIMER_FREQ / 1e6)) without using floating point
292	 * and without any avoidable overflows.
293	 */
294	sec = n / 1000000;
295	usec = n - sec * 1000000;
296	ticks_left = sec * TIMER_FREQ
297		     + usec * (TIMER_FREQ / 1000000)
298		     + usec * ((TIMER_FREQ % 1000000) / 1000) / 1000
299		     + usec * (TIMER_FREQ % 1000) / 1000000;
300
301	while (ticks_left > 0) {
302		tick = getit(0, 0);
303#ifdef DELAYDEBUG
304		++getit_calls;
305#endif
306		if (tick > prev_tick)
307			ticks_left -= prev_tick - (tick - timer0_divisor);
308		else
309			ticks_left -= prev_tick - tick;
310		prev_tick = tick;
311	}
312#ifdef DELAYDEBUG
313	if (state == 1)
314		printf(" %d calls to getit() at %d usec each\n",
315		       getit_calls, (n + 5) / getit_calls);
316#endif
317}
318
319static void
320sysbeepstop(void *chan)
321{
322	outb(IO_PPI, inb(IO_PPI)&0xFC);	/* disable counter2 output to speaker */
323	release_timer2();
324	beeping = 0;
325}
326
327int
328sysbeep(int pitch, int period)
329{
330
331	if (acquire_timer2(TIMER_SQWAVE|TIMER_16BIT))
332		return -1;
333	disable_intr();
334	outb(TIMER_CNTR2, pitch);
335	outb(TIMER_CNTR2, (pitch>>8));
336	enable_intr();
337	if (!beeping) {
338	outb(IO_PPI, inb(IO_PPI) | 3);	/* enable counter2 output to speaker */
339		beeping = period;
340		timeout(sysbeepstop, (void *)NULL, period);
341	}
342	return 0;
343}
344
345/*
346 * RTC support routines
347 */
348static int
349bcd2int(int bcd)
350{
351	return(bcd/16 *	10 + bcd%16);
352}
353
354static int
355int2bcd(int dez)
356{
357	return(dez/10 *	16 + dez%10);
358}
359
360static void
361writertc(int port, int val)
362{
363	outb(IO_RTC, port);
364	outb(IO_RTC+1, val);
365}
366
367static int
368readrtc(int port)
369{
370	return(bcd2int(rtcin(port)));
371}
372
373void
374startrtclock()
375{
376	int s;
377
378	/* initialize 8253 clock */
379	outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
380
381	/* Correct rounding will buy us a better precision in timekeeping */
382	outb (IO_TIMER1, TIMER_DIV(hz)%256);
383	outb (IO_TIMER1, TIMER_DIV(hz)/256);
384	timer0_divisor = hardclock_divisor = TIMER_DIV(hz);
385
386	/* initialize brain-dead battery powered clock */
387	outb (IO_RTC, RTC_STATUSA);
388	outb (IO_RTC+1, rtc_statusa);
389	outb (IO_RTC, RTC_STATUSB);
390	outb (IO_RTC+1, RTCSB_24HR);
391	outb (IO_RTC, RTC_DIAG);
392	if (s = inb (IO_RTC+1))
393		printf("RTC BIOS diagnostic error %b\n", s, RTCDG_BITS);
394	writertc(RTC_DIAG, 0);
395}
396
397/*
398 * Initialize the time of day register,	based on the time base which is, e.g.
399 * from	a filesystem.
400 */
401void
402inittodr(time_t base)
403{
404	unsigned long	sec, days;
405	int		yd;
406	int		year, month;
407	int		y, m, s;
408
409	s = splclock();
410	time.tv_sec  = base;
411	time.tv_usec = 0;
412	splx(s);
413
414	/* Look	if we have a RTC present and the time is valid */
415	if (rtcin(RTC_STATUSD) != RTCSD_PWR)
416		goto wrong_time;
417
418	/* wait	for time update	to complete */
419	/* If RTCSA_TUP	is zero, we have at least 244us	before next update */
420	while (rtcin(RTC_STATUSA) & RTCSA_TUP);
421
422	days = 0;
423	year = readrtc(RTC_YEAR) + readrtc(RTC_CENTURY)	* 100;
424	if (year < 1970)
425		goto wrong_time;
426	month =	readrtc(RTC_MONTH);
427	for (m = 1; m <	month; m++)
428		days +=	daysinmonth[m-1];
429	if ((month > 2)	&& LEAPYEAR(year))
430		days ++;
431	days +=	readrtc(RTC_DAY) - 1;
432	yd = days;
433	for (y = 1970; y < year; y++)
434		days +=	DAYSPERYEAR + LEAPYEAR(y);
435	sec = ((( days * 24 +
436		  readrtc(RTC_HRS)) * 60 +
437		  readrtc(RTC_MIN)) * 60 +
438		  readrtc(RTC_SEC));
439	/* sec now contains the	number of seconds, since Jan 1 1970,
440	   in the local	time zone */
441
442	sec += tz.tz_minuteswest * 60;
443
444	s = splclock();
445	time.tv_sec = sec;
446	splx(s);
447	return;
448
449wrong_time:
450	printf("Invalid	time in	real time clock.\n");
451	printf("Check and reset	the date immediately!\n");
452}
453
454/*
455 * Write system	time back to RTC
456 */
457void resettodr()
458{
459	unsigned long	tm;
460	int		y, m, fd, r, s;
461
462	s = splclock();
463	tm = time.tv_sec;
464	splx(s);
465
466	/* First, disable clock	updates	*/
467	writertc(RTC_STATUSB, RTCSB_HALT | RTCSB_24HR);
468
469	/* Calculate local time	to put in CMOS */
470
471	tm -= tz.tz_minuteswest * 60 + adjkerntz;
472
473	writertc(RTC_SEC, int2bcd(tm%60)); tm /= 60;	/* Write back Seconds */
474	writertc(RTC_MIN, int2bcd(tm%60)); tm /= 60;	/* Write back Minutes */
475	writertc(RTC_HRS, int2bcd(tm%24)); tm /= 24;	/* Write back Hours   */
476
477	/* We have now the days	since 01-01-1970 in tm */
478	writertc(RTC_WDAY, (tm+4)%7);			/* Write back Weekday */
479	for (y=1970;; y++)
480		if ((tm	- DAYSPERYEAR -	LEAPYEAR(y)) > tm)
481			break;
482		else
483			tm -= DAYSPERYEAR + LEAPYEAR(y);
484
485	/* Now we have the years in y and the day-of-the-year in tm */
486	writertc(RTC_YEAR, int2bcd(y%100));		/* Write back Year    */
487	writertc(RTC_CENTURY, int2bcd(y/100));		/* ... and Century    */
488	if (LEAPYEAR(y)	&& (tm >= 31+29))
489		tm--;					/* Subtract Feb-29 */
490	for (m=1;; m++)
491		if (tm - daysinmonth[m-1] > tm)
492			break;
493		else
494			tm -= daysinmonth[m-1];
495
496	writertc(RTC_MONTH, int2bcd(m));		/* Write back Month   */
497	writertc(RTC_DAY, int2bcd(tm+1));		/* Write back Day     */
498
499	/* enable time updates */
500	writertc(RTC_STATUSB, RTCSB_PINTR | RTCSB_24HR);
501}
502
503#ifdef garbage
504/*
505 * Initialze the time of day register, based on the time base which is, e.g.
506 * from a filesystem.
507 */
508test_inittodr(time_t base)
509{
510
511	outb(IO_RTC,9); /* year    */
512	printf("%d ",bcd(inb(IO_RTC+1)));
513	outb(IO_RTC,8); /* month   */
514	printf("%d ",bcd(inb(IO_RTC+1)));
515	outb(IO_RTC,7); /* day     */
516	printf("%d ",bcd(inb(IO_RTC+1)));
517	outb(IO_RTC,4); /* hour    */
518	printf("%d ",bcd(inb(IO_RTC+1)));
519	outb(IO_RTC,2); /* minutes */
520	printf("%d ",bcd(inb(IO_RTC+1)));
521	outb(IO_RTC,0); /* seconds */
522	printf("%d\n",bcd(inb(IO_RTC+1)));
523
524	time.tv_sec = base;
525}
526#endif
527
528/*
529 * Wire clock interrupt in.
530 */
531void
532enablertclock()
533{
534	register_intr(/* irq */ 0, /* XXX id */ 0, /* flags */ 0, clkintr,
535		      HWI_MASK | SWI_MASK, /* unit */ 0);
536	INTREN(IRQ0);
537	register_intr(/* irq */ 8, /* XXX id */ 1, /* flags */ 0, rtcintr,
538		      SWI_CLOCK_MASK, /* unit */ 0);
539	INTREN(IRQ8);
540	outb(IO_RTC, RTC_STATUSB);
541	outb(IO_RTC+1, RTCSB_PINTR | RTCSB_24HR);
542}
543
544void
545cpu_initclocks()
546{
547	stathz = RTC_NOPROFRATE;
548	profhz = RTC_PROFRATE;
549	enablertclock();
550}
551
552void
553setstatclockrate(int newhz)
554{
555	if(newhz == RTC_PROFRATE) {
556		rtc_statusa = RTCSA_DIVIDER | RTCSA_PROF;
557	} else {
558		rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
559	}
560	outb(IO_RTC, RTC_STATUSA);
561	outb(IO_RTC+1, rtc_statusa);
562}
563