clock.c revision 1.17
1/*	$NetBSD: clock.c,v 1.39 1996/05/12 23:11:54 mycroft Exp $	*/
2
3/*-
4 * Copyright (c) 1993, 1994 Charles Hannum.
5 * Copyright (c) 1990 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * William Jolitz and Don Ahn.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 *    must display the following acknowledgement:
21 *	This product includes software developed by the University of
22 *	California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 *    may be used to endorse or promote products derived from this software
25 *    without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 *
39 *	@(#)clock.c	7.2 (Berkeley) 5/12/91
40 */
41/*
42 * Mach Operating System
43 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
44 * All Rights Reserved.
45 *
46 * Permission to use, copy, modify and distribute this software and its
47 * documentation is hereby granted, provided that both the copyright
48 * notice and this permission notice appear in all copies of the
49 * software, derivative works or modified versions, and any portions
50 * thereof, and that both notices appear in supporting documentation.
51 *
52 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
53 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
54 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
55 *
56 * Carnegie Mellon requests users of this software to return to
57 *
58 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
59 *  School of Computer Science
60 *  Carnegie Mellon University
61 *  Pittsburgh PA 15213-3890
62 *
63 * any improvements or extensions that they make and grant Carnegie Mellon
64 * the rights to redistribute these changes.
65 */
66/*
67  Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
68
69		All Rights Reserved
70
71Permission to use, copy, modify, and distribute this software and
72its documentation for any purpose and without fee is hereby
73granted, provided that the above copyright notice appears in all
74copies and that both the copyright notice and this permission notice
75appear in supporting documentation, and that the name of Intel
76not be used in advertising or publicity pertaining to distribution
77of the software without specific, written prior permission.
78
79INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
80INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
81IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
82CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
83LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
84NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
85WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
86*/
87
88/*
89 * Primitive clock interrupt routines.
90 */
91#include <sys/param.h>
92#include <sys/systm.h>
93#include <sys/time.h>
94#include <sys/kernel.h>
95#include <sys/device.h>
96
97#include <machine/cpu.h>
98#include <machine/intr.h>
99#include <machine/pio.h>
100#include <machine/cpufunc.h>
101
102#include <dev/isa/isareg.h>
103#include <dev/isa/isavar.h>
104#include <dev/ic/mc146818reg.h>
105#include <i386/isa/nvram.h>
106#include <i386/isa/timerreg.h>
107
108#include "pcppi.h"
109#if (NPCPPI > 0)
110#include <dev/isa/pcppivar.h>
111
112#define __BROKEN_INDIRECT_CONFIG /* XXX */
113#ifdef __BROKEN_INDIRECT_CONFIG
114int sysbeepmatch __P((struct device *, void *, void *));
115#else
116int sysbeepmatch __P((struct device *, struct cfdata *, void *));
117#endif
118void sysbeepattach __P((struct device *, struct device *, void *));
119
120struct cfattach sysbeep_ca = {
121	sizeof(struct device), sysbeepmatch, sysbeepattach
122};
123
124struct cfdriver sysbeep_cd = {
125	NULL, "sysbeep", DV_DULL
126};
127
128static int ppi_attached;
129static pcppi_tag_t ppicookie;
130#endif /* PCPPI */
131
132void	spinwait __P((int));
133void	findcpuspeed __P((void));
134int	clockintr __P((void *));
135int	gettick __P((void));
136void	sysbeep __P((int, int));
137int	rtcget __P((mc_todregs *));
138void	rtcput __P((mc_todregs *));
139static int yeartoday __P((int));
140int 	hexdectodec __P((int));
141int	dectohexdec __P((int));
142int	rtcintr __P((void *));
143
144__inline u_int mc146818_read __P((void *, u_int));
145__inline void mc146818_write __P((void *, u_int, u_int));
146
147#if defined(I586_CPU) || defined(I686_CPU)
148int pentium_mhz;
149#endif
150
151#define	SECMIN	((unsigned)60)			/* seconds per minute */
152#define	SECHOUR	((unsigned)(60*SECMIN))		/* seconds per hour */
153#define	SECDAY	((unsigned)(24*SECHOUR))	/* seconds per day */
154#define	SECYR	((unsigned)(365*SECDAY))	/* seconds per common year */
155
156__inline u_int
157mc146818_read(sc, reg)
158	void *sc;					/* XXX use it? */
159	u_int reg;
160{
161
162	outb(IO_RTC, reg);
163	return (inb(IO_RTC+1));
164}
165
166__inline void
167mc146818_write(sc, reg, datum)
168	void *sc;					/* XXX use it? */
169	u_int reg, datum;
170{
171
172	outb(IO_RTC, reg);
173	outb(IO_RTC+1, datum);
174}
175
176void
177startrtclock()
178{
179	int s;
180
181	findcpuspeed();		/* use the clock (while it's free)
182					to find the cpu speed */
183	/* initialize 8253 clock */
184	outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
185
186	/* Correct rounding will buy us a better precision in timekeeping */
187	outb(IO_TIMER1, TIMER_DIV(hz) % 256);
188	outb(IO_TIMER1, TIMER_DIV(hz) / 256);
189
190	/* Check diagnostic status */
191	if ((s = mc146818_read(NULL, NVRAM_DIAG)) != 0)	/* XXX softc */
192		printf("RTC BIOS diagnostic error %b\n", (unsigned int) s,
193		    NVRAM_DIAG_BITS);
194}
195
196int
197clockintr(arg)
198	void *arg;
199{
200	struct clockframe *frame = arg;		/* not strictly necessary */
201
202	hardclock(frame);
203	return 1;
204}
205
206int
207rtcintr(arg)
208	void *arg;
209{
210	struct clockframe *frame = arg;		/* not strictly neccecary */
211	u_int stat;
212
213	stat = mc146818_read(NULL, MC_REGC);
214	if (stat & MC_REGC_PF) {
215		statclock(frame);
216		return 1;
217	}
218	return 0;
219}
220
221int
222gettick()
223{
224	u_char lo, hi;
225
226	/* Don't want someone screwing with the counter while we're here. */
227	disable_intr();
228	/* Select counter 0 and latch it. */
229	outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
230	lo = inb(TIMER_CNTR0);
231	hi = inb(TIMER_CNTR0);
232	enable_intr();
233	return ((hi << 8) | lo);
234}
235
236/*
237 * Wait "n" microseconds.
238 * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
239 * Note: timer had better have been programmed before this is first used!
240 * (Note that we use `rate generator' mode, which counts at 1:1; `square
241 * wave' mode counts at 2:1).
242 */
243void
244delay(n)
245	int n;
246{
247	int limit, tick, otick;
248
249	/*
250	 * Read the counter first, so that the rest of the setup overhead is
251	 * counted.
252	 */
253	otick = gettick();
254
255#ifdef __GNUC__
256	/*
257	 * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler code so
258	 * we can take advantage of the intermediate 64-bit quantity to prevent
259	 * loss of significance.
260	 */
261	n -= 5;
262	if (n < 0)
263		return;
264	{register int m;
265	__asm __volatile("mul %3"
266			 : "=a" (n), "=d" (m)
267			 : "0" (n), "r" (TIMER_FREQ));
268	__asm __volatile("div %3"
269			 : "=a" (n)
270			 : "0" (n), "d" (m), "r" (1000000)
271			 : "%edx");}
272#else
273	/*
274	 * Calculate ((n * TIMER_FREQ) / 1e6) without using floating point and
275	 * without any avoidable overflows.
276	 */
277	n -= 20;
278	{
279		int sec = n / 1000000,
280		    usec = n % 1000000;
281		n = sec * TIMER_FREQ +
282		    usec * (TIMER_FREQ / 1000000) +
283		    usec * ((TIMER_FREQ % 1000000) / 1000) / 1000 +
284		    usec * (TIMER_FREQ % 1000) / 1000000;
285	}
286#endif
287
288	limit = TIMER_FREQ / hz;
289
290	while (n > 0) {
291		tick = gettick();
292		if (tick > otick)
293			n -= limit - (tick - otick);
294		else
295			n -= otick - tick;
296		otick = tick;
297	}
298}
299
300#if (NPCPPI > 0)
301int
302sysbeepmatch(parent, match, aux)
303	struct device *parent;
304#ifdef __BROKEN_INDIRECT_CONFIG
305	void *match;
306#else
307	struct cfdata *match;
308#endif
309	void *aux;
310{
311	return (!ppi_attached);
312}
313
314void
315sysbeepattach(parent, self, aux)
316	struct device *parent, *self;
317	void *aux;
318{
319	printf("\n");
320
321	ppicookie = ((struct pcppi_attach_args *)aux)->pa_cookie;
322	ppi_attached = 1;
323}
324#endif
325
326void
327sysbeep(pitch, period)
328	int pitch, period;
329{
330#if (NPCPPI > 0)
331	if (ppi_attached)
332		pcppi_bell(ppicookie, pitch, period, 0);
333#endif
334}
335
336unsigned int delaycount;	/* calibrated loop variable (1 millisecond) */
337
338#define FIRST_GUESS   0x2000
339
340void
341findcpuspeed()
342{
343	int i;
344	int remainder;
345
346	/* Put counter in count down mode */
347	outb(TIMER_MODE, TIMER_SEL0 | TIMER_16BIT | TIMER_RATEGEN);
348	outb(TIMER_CNTR0, 0xff);
349	outb(TIMER_CNTR0, 0xff);
350	for (i = FIRST_GUESS; i; i--)
351		;
352	/* Read the value left in the counter */
353	remainder = gettick();
354	/*
355	 * Formula for delaycount is:
356	 *  (loopcount * timer clock speed) / (counter ticks * 1000)
357	 */
358	delaycount = (FIRST_GUESS * TIMER_DIV(1000)) / (0xffff-remainder);
359}
360
361#if defined(I586_CPU) || defined(I686_CPU)
362void
363calibrate_cyclecounter()
364{
365	unsigned long long count, last_count;
366#ifdef NTP
367	extern long time_precision;
368#endif
369
370	__asm __volatile(".byte 0xf, 0x31" : "=A" (last_count));
371	delay(1000000);
372	__asm __volatile(".byte 0xf, 0x31" : "=A" (count));
373	pentium_mhz = ((count - last_count) + 500000) / 1000000;
374#ifdef NTP
375	time_precision = 1;	/* XXX */
376#endif
377}
378#endif
379
380void
381cpu_initclocks()
382{
383	stathz = 128;
384	profhz = 1024;
385
386	/*
387	 * XXX If you're doing strange things with multiple clocks, you might
388	 * want to keep track of clock handlers.
389	 */
390	(void)isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK, clockintr,
391	    0, "clock");
392	(void)isa_intr_establish(NULL, 8, IST_PULSE, IPL_CLOCK, rtcintr,
393	    0, "rtc");
394
395	mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
396	mc146818_write(NULL, MC_REGB, MC_REGB_24HR | MC_REGB_PIE);
397}
398
399int
400rtcget(regs)
401	mc_todregs *regs;
402{
403	if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
404		return (-1);
405	MC146818_GETTOD(NULL, regs);			/* XXX softc */
406	return (0);
407}
408
409void
410rtcput(regs)
411	mc_todregs *regs;
412{
413	MC146818_PUTTOD(NULL, regs);			/* XXX softc */
414}
415
416static int month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
417
418static int
419yeartoday(year)
420	int year;
421{
422
423	return (((year % 4) == 0 &&
424		 ((year % 100) != 0 || (year % 400) == 0))? 366 : 365);
425}
426
427int
428hexdectodec(n)
429	int n;
430{
431
432	return (((n >> 4) & 0x0f) * 10 + (n & 0x0f));
433}
434
435int
436dectohexdec(n)
437	int n;
438{
439
440	return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f));
441}
442
443static int timeset;
444
445/*
446 * Initialize the time of day register, based on the time base which is, e.g.
447 * from a filesystem.
448 */
449void
450inittodr(base)
451	time_t base;
452{
453	mc_todregs rtclk;
454	time_t n;
455	int sec, min, hr, dom, mon, yr;
456	int i, days = 0;
457	int s;
458
459	/*
460	 * We mostly ignore the suggested time and go for the RTC clock time
461	 * stored in the CMOS RAM.  If the time can't be obtained from the
462	 * CMOS, or if the time obtained from the CMOS is 5 or more years
463	 * less than the suggested time, we used the suggested time.  (In
464	 * the latter case, it's likely that the CMOS battery has died.)
465	 */
466
467	if (base < 15*SECYR) {	/* if before 1985, something's odd... */
468		printf("WARNING: preposterous time in file system\n");
469		/* read the system clock anyway */
470		base = 17*SECYR + 186*SECDAY + SECDAY/2;
471	}
472
473	s = splclock();
474	if (rtcget(&rtclk)) {
475		splx(s);
476		printf("WARNING: invalid time in clock chip\n");
477		goto fstime;
478	}
479	splx(s);
480
481	sec = hexdectodec(rtclk[MC_SEC]);
482	min = hexdectodec(rtclk[MC_MIN]);
483	hr = hexdectodec(rtclk[MC_HOUR]);
484	dom = hexdectodec(rtclk[MC_DOM]);
485	mon = hexdectodec(rtclk[MC_MONTH]);
486	yr = hexdectodec(rtclk[MC_YEAR]);
487	yr = (yr < 70) ? yr+100 : yr;
488
489	n = sec + 60 * min + 3600 * hr;
490	n += (dom - 1) * 3600 * 24;
491
492	if (yeartoday(yr) == 366)
493		month[1] = 29;
494	for (i = mon - 2; i >= 0; i--)
495		days += month[i];
496	month[1] = 28;
497	for (i = 70; i < yr; i++)
498		days += yeartoday(i);
499	n += days * 3600 * 24;
500
501	n += tz.tz_minuteswest * 60;
502	if (tz.tz_dsttime)
503		n -= 3600;
504
505	if (base < n - 5*SECYR)
506		printf("WARNING: file system time much less than clock time\n");
507	else if (base > n + 5*SECYR) {
508		printf("WARNING: clock time much less than file system time\n");
509		printf("WARNING: using file system time\n");
510		goto fstime;
511	}
512
513	timeset = 1;
514	time.tv_sec = n;
515	time.tv_usec = 0;
516	return;
517
518fstime:
519	timeset = 1;
520	time.tv_sec = base;
521	time.tv_usec = 0;
522	printf("WARNING: CHECK AND RESET THE DATE!\n");
523}
524
525/*
526 * Reset the clock.
527 */
528void
529resettodr()
530{
531	mc_todregs rtclk;
532	time_t n;
533	int diff, i, j;
534	int s;
535
536	/*
537	 * We might have been called by boot() due to a crash early
538	 * on.  Don't reset the clock chip in this case.
539	 */
540	if (!timeset)
541		return;
542
543	s = splclock();
544	if (rtcget(&rtclk))
545		bzero(&rtclk, sizeof(rtclk));
546	splx(s);
547
548	diff = tz.tz_minuteswest * 60;
549	if (tz.tz_dsttime)
550		diff -= 3600;
551	n = (time.tv_sec - diff) % (3600 * 24);   /* hrs+mins+secs */
552	rtclk[MC_SEC] = dectohexdec(n % 60);
553	n /= 60;
554	rtclk[MC_MIN] = dectohexdec(n % 60);
555	rtclk[MC_HOUR] = dectohexdec(n / 60);
556
557	n = (time.tv_sec - diff) / (3600 * 24);	/* days */
558	rtclk[MC_DOW] = (n + 4) % 7;  /* 1/1/70 is Thursday */
559
560	for (j = 1970, i = yeartoday(j); n >= i; j++, i = yeartoday(j))
561		n -= i;
562
563	rtclk[MC_YEAR] = dectohexdec(j - 1900);
564
565	if (i == 366)
566		month[1] = 29;
567	for (i = 0; n >= month[i]; i++)
568		n -= month[i];
569	month[1] = 28;
570	rtclk[MC_MONTH] = dectohexdec(++i);
571
572	rtclk[MC_DOM] = dectohexdec(++n);
573
574	s = splclock();
575	rtcput(&rtclk);
576	splx(s);
577}
578
579void
580setstatclockrate(arg)
581	int arg;
582{
583	if (arg == stathz)
584		mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
585	else
586		mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_1024_Hz);
587}
588